grid布局能够以最简单的DOM结构实现复杂的布局,也可以让我们不必改变结构的情况下更灵活的扩展布局,相对于flex更加灵活,非常强大,现在让我们一起学习下吧。
什么是grid?
grid即网格,是一组相交的水平线和垂直线,它定义了网格的列和行。我们可以将网格元素放置在与这些行和列相关的位置上。CSS 网格布局具有固定的位置和弹性的轨道大小,创建轨道大小可以是固定的像素单位,也可以是弹性的,比如百分比或专门的轨道尺寸单位fr
来创建。
fr
单位:代表网格容器中可用空间的一等份,会随着可用空间增长和收缩。
grid兼容性
grid兼容性如下图
在最新版本的Chrome、Firefox和Edge中,CSS Grid已经完全实现。然而,在使用其他浏览器,比如Safari和IE时,就要小心了。Safari 10.1 +、iOS Safari 10.3 + 和 IE 11 及更早的版本不支持CSS Grid。
那么如果使用grid该如何处理其兼容性呢?以下有两个方案供大家参考:
1、flexbox回退方案
可以考虑使用 Flexbox 做为一个回退方案,一旦你在一个使用 flex
属性上使用 grid 布局,flex就会失效。同时引入Autoprefixer插件。Autoprefixer能够自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀,能够处理flexbox在不同浏览器的flexbugs
举个栗子:
.container {
/* Grid 属性 */
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(4, 1fr);
/* 不支持grid时的兼容处理 */
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
2、CSS特效查询(Feature Queries)
Flexbox在不同的浏览器上,相同属性名相同属性值在页面中的表现也可能不一致。因此对于处理grid兼容性,也可以选择使用css特性查询。
css特性查询是使用css的@supports,@supports用于检测浏览器是否支持参数中的属性属性值,如果支持则渲染花括号中的css代码,类似于:
@supports (display: grid) {
// code that will only run if CSS Grid is supported by the browser
}
注意:IE10、IE11是不支持@supports规则,所以压根不会进入这个条件判断,花括号中的css代码是不会渲染的。
举个例子:
- 首先是给旧浏览器做支持,准备一套Fallback method,保证在所有浏览器上都是工作的:
.wrapper{
overflow:auto;
}
.item {
float:left;
width:33.3%;
}
- 再给支持Grid的浏览器做覆盖,覆盖代码分两部分,一部分是直接放入对旧浏览没有影响的:
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
因为旧浏览器不支持Grid布局,Grid相关属性旧浏览器都无法理解。在支持的浏览器中使得item由floated item转为grid item,这样的覆盖行为由css规定,更多覆盖规则感兴趣可以去了解一下。另一部分是直接放入对旧浏览器是有影响的,要做css特性查询:
@supports (display: grid) {
.item {
width: auto; // 覆盖原有的width:33.3%。
}
}
没错,这里的实践得写两套样式。这时大家会想,这么麻烦,写一套支持所有浏览器的不就得了,干嘛非得用Grid?这是个很实际的问题,以下有几个场景建议使用Grid:
- 项目得支持IE10、IE11等旧浏览器,但是开发者想尝鲜Grid布局;
- 项目周期会很长,可能现在不支持Grid布局的浏览器,以后就支持了;
- 要实现的效果不使用Grid布局很难实现,且对在旧浏览器中访问效果要求不高,能看就行
容器属性
创建一个网格容器
我们通过在元素上声明 display:grid
或 display:inline-grid
来创建一个网格容器。这个元素的所有直系子元素将成为网格元素。
注意,设为网格布局以后,容器子元素(项目)的 float、display: inline-block、display: table-cell、vertical-align 和 column-* 等设置都将失效。但是 position 是不会失效的。
DOM结构
<div class="box">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
对应的css
.box{
display: grid;
}
.item{
width:80px;
height:80px;
}
.box .item:nth-of-type(2n) {
background-color: green;
}
.box .item:nth-of-type(2n+1) {
background-color: pink;
}
此时子元素将默认单行排列默认情况下,当父元素设置grid
, 默认grid-template-columns: 1fr
,grid-template-rows:1fr
网格布局
网格布局主要由colums
与rows
来定义列与行
- 九宫格
.box {
background: beige;
display: grid;
grid-template-columns: 33.3% 33.3% 33.3%;
grid-template-rows: 200px 200px 200px;
}
.box .item:nth-of-type(2n) {
background-color: pink;
}
.box .item:nth-of-type(2n+1) {
background-color: LightBlue;
}
以上是一个宽度自适应,每个高度为固定200px
的九宫格,自动换行从第4个元素开始。也就是grid-template-columns
确定了3列,每一列占据整个屏幕的33.3%
,而高度为固定高度200px
。
grid-template-columns: 1fr 1fr 1fr
与之效果是一样的。
1fr
是网格的轨道单位
,会随着可用空间增长和收缩,假设你要设置一个2fr 1fr 1fr
,那么第一个元素会是第二个元素的两倍
grid-template-columns & grid-template-rows
在以上例子中,我们使用grid-template-columns
控制了网格列数,通过这个属性,我们可以不用单独对子元素进行控制,你希望得到几列,就设置几个值,可以是百分比,也可以是固定像素。使用grid-template-rows
控制子元素的高度,同样可以是百分比,也可以是固定像素
.box{
background: beige;
display: grid;
grid-template-columns: 200px 100px 1fr 1fr;
grid-template-rows: 200px 200px 200px 200px;
}
.box .item:nth-of-type(3n) {
background-color: pink;
}
.box .item:nth-of-type(3n+1) {
background-color: lightBlue;
}
.box .item:nth-of-type(3n+2) {
background-color: green;
}
grid-columns-gap
控制列之间的间距,间距使用的空间会在 使用弹性长度 fr 的轨道的空间计算前就被留出来
.box {
background: beige;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 200px 200px 200px;
grid-column-gap: 10px; /* 列之间10个像素*/
}
.box .item:nth-of-type(2n) {
background-color: pink;
}
.box .item:nth-of-type(2n+1) {
background-color: lightBlue;
}
grid-rows-gap
控制行之间的间距
.box{
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 200px 200px 200px;
grid-row-gap: 10px; /* 行之间的间距 */
}
.box {
display: grid;
padding:10px;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 200px 200px 200px;
grid-column-gap: 10px;
grid-row-gap: 10px;
padding: 10px;
}
给父元素添加padding: 10px;
,然后分别设置grid-column-gap: 10px;
,grid-row-gap:10
就可以实现间距相等
也可以用column-gap
与row-gap
来替代grid-column-gap
与grid-row-gap
gap的简写
gap:10px 20px
是row-gap:10px
与column-gap:20px
的简写。如果省略了第二个值,浏览器认为第二个值等于第一个值。
注意:为了兼容浏览器,比如火狐 52-61,还需要使用带有前缀的属性grid-gap
repeat(repeat, values)
Repeat语句可以创建重复轨道,比如网格将有共计 10 个轨道,为 1 个1fr
轨道后面跟着 1 个2fr
轨道,该模式重复 5 次,就可以写成grid-template-columns: repeat(5, 1fr 2fr);
。
第一个值 repeat 可以是固定的数字 (n),表示重复 n 次,也可以是auto-fit
或 auto-fill
,表示自动填充,容纳不下的放到下一行。
在以上我们使用grid-template-columns:1fr 1fr 1fr
的写法,也可以利用repeat来简写
.box{
display: grid;
/* grid-template-columns:1fr 1fr 1fr */
grid-template-columns: repeat(3,1fr);
/*grid-template-rows: 200px 200px 200px*/
grid-template-rows: repeat(3, 200px);
grid-column-gap: 10px;
grid-row-gap: 10px;
padding: 10px;
}
注意:auto-fit和auto-fill通常没有区别,只有在容器宽度大于子元素最小宽度总和时才有显示区别(配合minmax使用时)
比如:
grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
再看一下auto-fit:
grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
根据上面例子,可以看到auto-fit
和 auto-fill
都会以最小的宽度创建单元格,创建完成以后,如果有多余的单元格,auto-fit
会把这些多余的单元格均分给每一网格项,再重新创建单元格,而auto-fill
不会。
minmax(min, max)
- 用来定义一个范围,最小值是min,最大值是max。
- max可以设置为fr,min不可以。
- 如果max值小于min,则该值会被视为min值
.box{
display: grid;
/* grid-template-columns: 1fr 1fr 1fr; */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-column-gap: 10px;
grid-row-gap: 10px;
padding: 10px;
}
由上面可见:每个item最小200px,当缩小屏幕,item宽度逐渐压缩,当小于200px时,会逐行展示,当拉大屏幕,由于设置了auto-fit,item跟随屏幕自适应排列
grid-auto-columns & grid-auto-rows
指定自动生成的网格轨道的大小。
当定义的网格不够容纳所有网格项,多出的网格项另起一行排列,或者给某一个网格项指定到定义好的网格区域的外部,这个时候,浏览器会自动生成多余的网格,以便放置项目。
grid-auto-columns 和 grid-auto-rows 就可以用来定义这些自动生成的多余的网格的大小。
.box {
display: grid;
width: 500px;
height: 400px;
background: beige;
grid-template-columns: 40px auto 50px 40px;
grid-template-rows: 300px auto;
grid-auto-rows: 50px;
grid-gap: 5px;
}
如上面的代码所示,定义的是 2 行 4 列一共 8 个单元格,但有网格项10 个,所以最终生成的网格是 3 行 4 列,grid-auto-rows: 50px;
让最后一行的行高为 50px。
grid-auto-columns 和 grid-auto-rows 的默认值为auto
。
grid-template-areas
命名网格区域名称
* {
padding: 0;
margin: 0;
}
.app {
background: beige;
display: grid;
grid-template-columns: 200px 1fr 1fr;
grid-auto-rows: minmax(100px, auto);
/** grid-template-areas给容器划分区域并命名**/
grid-template-areas:
"hd hd hd"
"sd main main"
"ft ft ft";
}
header {
/**grid-area 属性指定项目放在哪一个区域。**/
grid-area: hd;
background-color: lightBlue;
}
aside {
grid-area: sd;
background-color: pink;
}
main {
grid-area: main;
background-color: yellow;
}
footer {
grid-area: ft;
background-color: palegreen;
}
如果某些区域不需要利用,则可以使用“点”(.)表示,例如:
grid-template-areas: "a b c" "d d f" "g . i";
justify-items & align-items
justify-items 定义了网格布局中项目的水平对齐方式,align-items 定义了竖直对齐方式。
- start:内容与网格区域的左端对齐
- end: 内容与网格区域的右端对齐
- center: 内容位于网格区域的中间位置
- stretch: 内容宽度占据整个网格区域空间(默认值)
以下展示justify-items不同取值效果,align-items类似
这个与 flex 布局的有些区别,flex 布局的对齐方式分主轴和交叉轴,主轴方向的改变影响属性作用的方向。而 grid 布局是二维的,行列方向是固定的,所以说到 justify 一定就是指行(水平)方向,align 就是指列(竖直)方向。
place-items
place-items 属性是 align-items 属性和 justify-items 属性的合并简写形式。 语法是 place-items: <align-items> <justify-items>;
如果省略第二个值,则浏览器认为与第一个值相等。
有了这个属性,水平垂直居中就很容易了
justify-content & align-content
justify-content 定义了网格容器中,网格区域在水平方向上的对齐方式,align-content 定义了网格区域在竖直方向上的对齐方式。
属性值比 items 要多:
- start:网格区域与容器的左端对齐
- end: 网格区域与容器的右端对齐
- center: 网格区域位于容器的中间位置
- stretch: 网格区域占据整个容器空间(默认值)
- space-around:网格项之间设置等宽的间隙,与容器边框的间隙是项之间间隙的一半
- space-between: 网格项之间设置等宽的间隙,与容器边框没有间隙
- space-evenly:网格项之间和与容器边框设置等宽的间隙
justify-content:end
举例
justify-content:space-around
举例
place-content
place-content 属性是 align-content 和 justify-content 的合并简写形式。语法是 place-content: <align-content> <justify-content>;
项目属性
grid-column-start & grid-column-end
控制列的跨度。 start:开始显示网格的地方, end:停止显示网格的地方,如果end=start,则认为占据第start列,与end=start+1效果一样
/**从列线1-列线2-列线3-列线4,跨越了3列轨道**/
.box .item:nth-of-type(1) {
grid-column-start: 1;
grid-column-end: 4;
}
可以看到item1跨越了1-3列
grid-row-start & grid-row-end
控制行的跨度
/**从行线3-行线3,占据第三行轨道**/
.box .item:nth-of-type(1) {
grid-row-start: 3;
grid-row-end: 3;
grid-column-start: 1;
grid-column-end: 3;
}
可以看到item1位于第三行,跨越1-3列
grid-column & grid-row
我们可以通过
grid-column:1/3
来简grid-column-start:1;grid-column-end:3
,
grid-row:1/3
来简写grid-row-start:1;grid-row-end:3
grid-area
grid-area是grid-row-start
、grid-column-start
、grid-row-end
、grid-column-end
的简写
.box .item:nth-of-type(2) {
grid-area: 1 / 2 / 2 / 2;
}
- 实现一个
header
,slide
,main
,footer
的布局
<div class="app">
<header>header</header>
<aside>slide</aside>
<main>main</main>
<footer>footer</footer>
</div>
对应的css如下
.app {
background: beige;
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 30px calc(100vh - 90px) 60px;
}
header {
grid-column: 1 / 3;
background-color: lightBlue;
}
aside {
grid-column: 1 / 2;
background-color: pink;
}
main {
grid-column: 2 / 3;
background-color: lightYellow;
}
footer {
grid-column: 1 / 3;
background-color: palegreen;
}
justify-self 和 align-self
justify-self 设置某个元素中内容的水平对齐方式。align-self 设置元素中内容的竖直对齐方式。
- start:内容与网格项的左端对齐
- end: 内容与网格项的右端对齐
- center: 内容位于网格项的中间位置
- stretch: 内容宽度占据整个网格项空间(默认值)
place-self
place-self 属性是 align-self 属性和 justify-self 属性的合并简写形式。
语法为:<align-self> <justify-self>