CSS Grid网格布局

Grid 布局与 Flex 布局有一定的相似性,都可以指定容器内部多个项目的位置。但是,它们也存在重大区别。同时跟表格 <table> 也有些相似,也有 单元格 的概念。

Flex 布局是轴线布局,只能指定 “项目” 针对轴线的位置,可以看作是一维布局。Grid 布局则是将容器划分成 “行” 和 “列”,产生单元格,然后指定 “项目所在” 的单元格,可以看作是二维布局。Grid 布局是继 Flex 布局之后的又一布局利器。

基本概念

容器和项目

采用网格布局的区域,称为 “容器”(container)。容器内部采用网格定位的子元素,称为 “项目”(item)。

1
2
3
4
5
6
7
8
<div class="container">
<div class="item item-1"> 1 </div>
<div class="item"> <span> 2 </span> </div>
<div class="item"> 3 </div>
<div class="item"> 4 </div>
<div class="item"> 5 </div>
<div class="item"> 6 </div>
</div>

上面代码中,最外层的 <div> 元素就是 容器,内层的 6 个 <div> 元素就是 项目

注意:项目只能是容器的顶层子元素,不包含项目的子元素,比如上面代码的 <span> 元素 和文本内容就不是项目。Grid 布局只对项目生效。

行和列

容器里面的 水平区域 称为 “行”(row),垂直区域 称为 “列”(column)。

demo-1

上图中,水平的深色区域就是 “行”,垂直的深色区域就是 “列”。

单元格

行和列的交叉区域,称为 “单元格”(cell)。

正常情况下,n行 和 m列 会产生 n x m 个单元格。比如,3行3列 会产生 9 个单元格。

网格线

划分网格的线,称为 “网格线”(grid line)。水平网格线划分出行,垂直网格线划分出列。

正常情况下,n 行有 n + 1 根水平网格线,m 列有 m + 1 根垂直网格线,比如三行就有四根水平网格线。

demo-2

上图是一个 4 x 4 的网格,共有 5 根水平网格线和 5 根垂直网格线。

容器规则

Grid 布局的规则分成两类。一类定义在容器上面,称为 容器规则;另一类定义在项目上面,称为项目规则。这部分先介绍 容器规则

display

将容器元素的 display 规则设置为 grid 即可启用 网格布局。

1
2
3
.grid {
display: grid;
}

默认情况下,容器元素都是块级元素,但也可以设成行内元素。

1
2
3
.grid {
display: inline-grid;
}

注意,设为网格布局以后,容器的float、display: inline-block、display: table-cell、vertical-align和column-*等设置都将失效。

grid-template-rows

grid-template-columns

容器指定了网格布局以后,接着就要划分行和列。grid-template-columns 规则定义每一列的列宽,grid-template-rows 规则定义每一行的行高。

1
2
3
4
5
6
7
.box-1 {
/* 定义网格的行:这里为 3 行 ,每行的高度分别为:100px 100px 100px */
grid-template-rows: 100px 100px 100px;

/* 定义网格的列:这里为 3 列 ,每列的宽度分别为:100px 100px 100px */
grid-template-columns: 100px 100px 100px;
}

demo-3

如上图,使用的是绝对单位,定义了 3*3 的网格布局,列宽和行高 还可以使用百分比,如下:

1
2
3
4
5
.box-2 {
height: 300px;
grid-template-rows: 33.33% 33.33% 33.33%;
grid-template-columns: 33.33% 33.33% 33.33%;
}

demo-4

使用百分比单位时,会根据容器的宽高按比例自动分配行高和列宽。

网格线的名称

grid-template-columnsgrid-template-rows 的值中可以设置每条网格线的名称。
下面代码指定网格布局为 2行 x 3列,因此有 4 根垂直网格线和 3 根水平网格线。方括号里面依次是这 7 根线的名字。
网格布局允许同一根线有多个名字,比如 [fifth-line row-5]

1
2
3
4
5
6
7
8
<div class="grid box-3">
<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>
1
2
3
4
5
.box-3 {
height: 200px;
grid-template-rows: [r1] 50% [r2] 50% [r3];
grid-template-columns: [c1] 33.33% [c2] 33.33% [c3] 33.33% [c4];
}

demo-5

grid-row-gap

grid-column-gap

grid-row-gap 定义网格中行与行之间的间距。
grid-column-gap 定义网格中列与列之间的间距。

1
2
3
4
5
6
7
8
<div class="grid box-4">
<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>
1
2
3
4
5
6
.box-4 {
grid-template-rows: 100px 100px;
grid-template-columns: 200px 200px 200px;
grid-row-gap: 50px;
grid-column-gap: 50px;
}

demo-6

如上图,定义了 2*3 的网格,行与行之间的间隔为 50px ,列与列之间的间隔为 50px

grid-gap

grid-gapgrid-row-gapgrid-column-gap 的简写属性。

1
grid-gap: [grid-row-gap] [grid-column-gap];

如果 grid-gap 省略了第二个值,浏览器认为第二个值等于第一个值。

根据最新标准,上面三个属性名的 grid- 前缀已经删除,grid-column-gapgrid-row-gap 写成 column-gaprow-gapgrid-gap 写成 gap

grid-template-areas

网格布局允许指定 “区域”(area),一个区域由单个或多个单元格组成。grid-template-areas 属性用于定义区域。
下面代码先划分出 6 个单元格,然后将其定名为 a 到 f 的 6 个区域,分别对应这 6 个单元格。 如果某些区域不需要利用,则使用 “点”(.)表示。

1
2
3
4
5
6
7
8
<div class="grid box-5">
<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>
1
2
3
4
5
6
.box-5 {
grid-template-rows: 100px 100px;
grid-template-columns: 200px 200px 200px;
grid-template-areas: 'a b c'
'd e f';
}

demo-7

grid-auto-flow

划分网格以后,容器中的子元素 (项目) 会按照顺序,自动放置在每一个网格。
默认的放置顺序是 “先行后列”,即先填满第一行,再开始放入第二行,以此类推。

1
grid-auto-flow: row;

demo-8

grid-auto-flow 的值设置为 column 后,变为 “先列后行”。

1
grid-auto-flow: column;

demo-9

grid-auto-flow 的值还可以设成 row densecolumn dense。这两个值主要用于,某些项目指定位置以后,剩下的项目怎么自动放置。

让 1号项目 和 2号项目 各占据两个单元格,然后在默认的 grid-auto-flow: row 情况下,会产生下面这样的布局。

1号项目 后面的位置是空的,因为 1号项目 的后方不够 2号项目 放置,并且 3号项目 默认跟着 2号项目,所以会排在2号项目后面。

1
2
3
4
5
6
7
8
<div class="grid box-8">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
.box-8 {
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
grid-auto-flow: row;
}

.box-8 .item-1 {
background-color: #ef342a;
grid-column-start: 1;
grid-column-end: 3;
}

demo-10

grid-auto-flow 的值设置为 row dense 后,表示 “先行后列”,并且尽可能紧密填满,尽量不出现空格。

demo-11

如果将设置改为 column dense,表示 “先列后行”,并且尽量填满空格。

demo-12

项目在单元格中的对齐方式

justify-items

设置项目在单元格中水平方向上的位置(左 start 中 center 右 end 拉伸(默认) stretch)。

align-items

设置项目在单元格中垂直方向上的位置(左 start 中 center 右 end 拉伸(默认) stretch)。

1
2
3
4
start:对齐单元格的起始边缘。 
end:对齐单元格的结束边缘。
center:单元格内部居中。
stretch:拉伸,占满单元格的整个宽度(默认值)。

place-items

place-items 属性是 align-items 属性和 justify-items 属性的合并简写形式。

1
place-items: [align-items] [justify-items];

单元格的内容左对齐,效果如下图。

1
justify-items: start;

demo-13

单元格的内容头部对齐,效果如下图。

1
align-items: start;

demo-14

项目在容器中的对齐方式

justify-content

justify-content 属性可以定义整个内容区域在容器里面的水平放置位置(左中右)。

align-content

align-content 属性可以定义整个内容区域在容器里面的垂直放置位置(左中右)。

上面两个属性都可以使用如下值:

1
2
3
4
5
6
7
start : 网格线开始位置,靠左或靠上。
end : 网格线结束位置,靠右或靠下。
center : 垂直或水平居中。
stretch : 项目大小没有指定时,拉伸占据整个网格容器。
space-around : 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与容器边框的间隔大一倍。
space-between : 项目与项目的间隔相等,项目与容器边框之间没有间隔。
space-evenly : 项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。

这两个属性的写法完全相同,都可以取下面这些值。(下面的图都以 justify-content 属性为例,align-content 属性的图完全一样,只是将水平方向改成垂直方向。)

  • start 对齐容器的起始边框。

demo-15

  • end : 网格线结束位置,靠右或靠下。

demo-16

  • center : 垂直或水平居中。

demo-17

  • stretch : 项目大小没有指定时,拉伸占据整个网格容器。

demo-18

  • space-around : 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与容器边框的间隔大一倍。

demo-19

  • space-between : 项目与项目的间隔相等,项目与容器边框之间没有间隔。

demo-20

  • space-evenly : 项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。

demo-21

place-content

place-content 属性是 align-content 属性和 justify-content 属性的合并简写形式。

1
place-content: [align-content] [justify-content];

grid-auto-columns

grid-auto-rows

grid-auto-columnsgrid-auto-rows 属性用来设置,浏览器自动创建的多余网格的列宽和行高。用法和 grid-template-columns 、grid-template-row 一毛一样。

1
2
3
4
5
6
7
8
 <div class="box-11 grid">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
1
2
3
4
5
6
7
8
9
.box-11 {
grid-template-columns: repeat(3,100px);
grid-template-rows: repeat(2,100px);
}

.box-11 .item-1 {
grid-column-start: 1;
grid-column-end: 3;
}

demo-22

如上图所示,区域 1 占据了 2 个单元格,导致第6个单元格无法放置在原本的网格内,浏览器自动根据第6块内容的大小创建了一个单元格。该单元格的宽高取决于这块内容自身的大小。

当我们设置了 grid-auto-columnsgrid-auto-rows 属性时,浏览器则会根据这两个属性值去生成多余单元格的大小。

1
2
3
4
.box-11 {
grid-auto-columns: 100px;
grid-auto-rows: 100px;
}

如下图:

demo-23

grid-template

grid-template 属性是 grid-template-columns、grid-template-rowsgrid-template-areas 这三个属性的合并简写形式。

grid

grid 属性是 grid-template-rows、grid-template-columns、grid-template-areas、grid-auto-rows、grid-auto-columns、grid-auto-flow 这六个属性的合并简写形式。

函数

minmax()

使用 minmax() 函数约定长度范围。

1
2
3
4
5
<div class="grid box-1">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
1
2
3
4
5
.box-1 {
height: 100px;
/* 共定义了 3 列,第一、二列 占容器总宽度的 1/3 。第3列 最少占200px 宽度,最大占 容器总宽度的 1/3 */
grid-template-columns: 1fr 1fr minmax(200px , 1fr);
}

demo-24

上方代码规则定义了 1*3 的网格,第 3 列单元格 使用了 minmax() 函数定义该列的列宽规则。表示,最小有 200px 宽,最大有 容器总宽度的 1/3。如下图,当缩小容器宽度的时候,前 2 列的宽度值由于是相对单位,会进行适应缩放,而第 3 列 则会 最小保持 200px 的列宽。

demo-25

repeat()

repeat() 函数可以某个值的次数。通常作用于 grid-template-rowsgrid-template-columns

第一个参数:重复的次数。第二个参数:要重复的值。

1
2
3
4
5
6
7
8
9
10
11
<div class="grid box-3">
<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>
1
2
3
4
5
6
.box-3 {
/* 使用 repeat() 函数 简化 重复值 */
height: 300px;
grid-template-rows: repeat( 3, 33.33% );
grid-template-columns: repeat( 3, 33.33% );
}

上面代码规则等同于:

1
2
3
4
5
.box-3 {
height: 300px;
grid-template-rows: 33.33% 33.33% 33.33%;
grid-template-columns: 33.33% 33.33% 33.33%;
}

demo-26

也可以使用 repeat() 函数重复某种模式。第一个参数:重复模式的次数。第二个参数:要重复的模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.box-4 {
/* 使用 repeat() 函数 以某种模式重复 */

grid-template-rows: repeat( 2, 200px 100px );
grid-template-columns: repeat( 2, 200px 100px );

/* 上面代码定义了 4 行 ,4 列 ,也就是有 4*4=16 的单元格 */
/*

拆分开来为:

grid-template-rows: 200px 100px 200px 100px
grid-template-columns: 200px 100px 200px 100px

第 1 个单元格的 高为 200px 宽为 200px
第 2 个单元格的 高为 200px 宽为 100px

......

*/
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="grid box-4">
<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 class="item">10</div>
<div class="item">11</div>
<div class="item">12</div>
<div class="item">13</div>
<div class="item">14</div>
<div class="item">15</div>
<div class="item">16</div>
</div>

demo-27

关键字

auto-fill

使用 auto-fill 关键字 控制自动填充。

如下代码规则,列数自动填充,直到放不下为止,且每列的宽度为 200px。

1
2
3
4
5
6
<div class="grid box-5">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
1
2
3
4
5
6
7
8
9
10
.box-5 {
grid-auto-rows: 100px;

/* 定义行高为 100px ,默认网格占据有 2 行的高度*/
grid-template-rows: repeat( 2 , 100px );

/* 列数自动填充,直到放不下为止,且每列的宽度为 200px */
grid-template-columns: repeat( auto-fill , 200px );

}

demo-28

fr

使用 fr 关键字表示比例关系。

如下代码规则,第二列为第一列的2倍宽。

1
2
3
4
5
6
<div class="grid box-6">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
1
2
3
4
5
6
.box-6 {
height: 300px;
/* 第2列的宽度为第1列的 2 倍 */
grid-template-columns: 1fr 2fr;
grid-template-rows: 100px 100px;
}

demo-29

fr 与 绝对单位 结合使用。第一列为固定宽度 200px,第3列宽为第2列的2倍。

1
2
3
4
5
6
7
8
<div class="grid box-7">
<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>
1
2
3
4
5
.box-7 {
/* 第3列的宽度为第2列的 2 倍 */
grid-template-columns: 200px 1fr 2fr;
grid-template-rows: 100px 100px;
}

demo-30

fr 之 圣杯布局

1
2
3
4
5
 <div class="grid box-8">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
1
2
3
4
5
.box-8 {
/* 经典的 高度固定,两端固定,中间自适应的三列布局 */
grid-template-columns: 200px 1fr 200px;
grid-template-rows: 100px;
}

demo-31

auto

auto 关键字,表示由浏览器自己决定宽度。

如下,第1、3列为固定100px宽度,中间的第2列宽度由浏览器自动计算,即自适应。

1
2
3
4
5
 <div class="grid box-2">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
1
2
3
4
.box-2{
height: 100px;
grid-template-columns: 100px auto 100px;
}

demo-32

项目规则

下面这些规则定义在项目上面。

grid-row-start

grid-row-end

grid-column-start

grid-column-end

项目的位置是可以指定的,具体方法就是指定项目的四个边框,分别定位在哪根网格线。

1
2
3
4
grid-row-start 用来设置 `上边框` 所在的网格线位置。
grid-row-end 用来设置 `下边框` 所在网格线的位置。
grid-column-start 用来设置 `左边框` 所在网格线的位置。
grid-column-end 用来设置 `右边框` 所在网格线的位置。
1
2
3
4
5
6
7
8
<div class="grid box box-1">
<div class="item item-1">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>
1
2
3
4
5
6
7
8
9
10
.box {
grid-template-columns: repeat(3,100px);
grid-template-rows: repeat(3,100px);
}

.box-1 .item-1 {
background-color: red;
grid-column-start: 2;
grid-column-end: 4;
}

demo-33

如上图:1号项目的左边框被设置在第2根垂直网格线上,右边框被设置在第4根垂直网格线上。使得 1 号项目横跨了 第2、3个单元格。

剩余项目则跟在其后自动排布。这时它们的位置由容器的 grid-auto-flow 属性决定,这个属性的默认值是 row,因此会”先行后列”进行排列。

这四个属性的值,除了指定为第几个网格线,还可以指定为网格线的名字。

1
2
3
4
.item-1 { 
grid-column-start: header-start;
grid-column-end: header-end;
}

这四个属性的值还可以使用 span 关键字,表示 “跨越”,即左右边框(上下边框)之间跨越多少个网格。

1
2
3
4
5
6
7
8
<div class="grid box box-2">
<div class="item item-1">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>

demo-34

如上图:第一个项目横跨了第一个和第二个单元格。
这与下面代码效果一样:

1
2
3
.item-1 { 
grid-column-end: span 2;
}

使用这四个属性,如果产生了项目的重叠,则使用 z-index 属性指定项目的重叠顺序。

grid-column

grid-row

grid-column 属性是 grid-column-start 和 grid-column-end 的合并简写形式,
grid-row 属性是 grid-row-start 和 grid-row-end 的合并简写形式。

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
.item-1 {
grid-column: 1 / 3;
grid-row: 1 / 2;
}
/* 等同于 */
.item-1 {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 2;
}

这两个属性之中,也可以使用 span 关键字,表示跨越多少个网格。

1
2
3
4
5
6
7
8
9
10
11
.item-1 { 
background: #b03532;
grid-column: 1 / 3;
grid-row: 1 / 3;
}
/* 等同于 */
.item-1 {
background: #b03532;
grid-column: 1 / span 2;
grid-row: 1 / span 2;
}

demo-35

斜杠以及后面的部分可以省略,默认跨越一个网格。

1
2
3
4
.item-1 { 
grid-column: 1;
grid-row: 1;
}

上面代码中,项目item-1占据左上角第一个网格。

grid-area

grid-area 属性指定项目放在哪一个区域。

1
2
3
4
5
6
7
8
9
10
11
 <div class="grid box-4">
<div class="item item-1">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>
1
2
3
4
5
6
7
8
9
10
11
12
.box-4 {
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
grid-template-areas: 'a b c'
'd e f'
'g h i';
}

.box-4 .item-1 {
background-color: #b03532;
grid-area: e;
}

demo-36

如上图:将 1 号项目放置在 e 区域中。

项目中内容的对齐方式

justify-self

设置单元格内容的水平位置(左中右),只作用于单个项目中。

align-self

设置单元格内容的垂直位置(上中下),只作用于单个项目中。

这两个属性都可以去下面这些值:

1
2
3
4
start:对齐单元格的起始边缘。 
end:对齐单元格的结束边缘。
center:单元格内部居中。
stretch:拉伸,占满单元格的整个宽度(默认值)。

下面是 justify-self: start 的例子:

demo-37

place-self

align-selfjustify-self 的简写属性。

1
place-self: [align-self] [justify-self]; 

如果省略第二个值,place-self 属性会认为这两个值相等。