网格布局(Grid)被视为当前最强大的CSS布局方案。
我需要的布局大致如下:

可以看到,此布局并不复杂,我们可以使用多种方式来实现它,但今天我的计划是使用Grid布局。
Grid布局将容器划分为“行”与“列”,产生单元格,然后指定“item”所在的单元格,因此也常被视为“二维布局”。
如名所示,最外层的元素作为容器(container),内部每一个最外层的元素作为一个单独的项目(item)。
<main>
<section>a</section>
<section>b</section>
<section>c</section>
</main>
Grid布局针对main生效,section作为item,其内部元素与布局无关。

我想这一张图已经非常明显地使用深色体现出行(row)与列(column)的区别,行和列是有交叉的。
行列交叉的区域,我们称之为Cell(单元格),如前言所示,我们将子元素放在单元格中。而深色区域,我们将之称为Grid line(网格线),通常n行m列,即可产生可供布局的n*m个Cell。
不要讲空白区域视为单元格,单元格始终是行和列相交产生的。
Grid布局属性分为定义在container上的容器属性,定义在item上的项目属性。
显示为container设置display: grid显示属性布局为grid。
div {
display: grid;
}
此时,container是一个单独的容器,默认是块级元素,也可以设置display: inline-grid为行内Grid布局,使其整体视为一个行内块级元素。
网格布局将使得子项(item)的
float、display: inline-block、display: table-cell、display: vertical-align、display: column-*等设置失效。
grid布局除了需要显示指定布局类型为grid外,还需要指定行和列的值。
grid-template-rows定义行高,有多少行就提供多少个值。
grid-template-column定义列宽,同样,有多少列就提供多少个值。
例如,如果我们要设置一个九宫格,则分别需要三行三列:
.container {
display: grid;
grid-template-rows: 100px 100px 100px;
grid-template-column: 100px 100px 100px;
}
如此一来配上item(css 提供一些颜色值):
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
我们可以得到一个九宫格布局:

除了使用px这样的绝对单位,也可以使用百分数,甚至可以使用repeat类函数简化赋值:
.container {
display: grid;
grid-template-columns: repeat(3, 33.33%);
grid-template-rows: repeat(3, 33.33%);
}
甚至是:
.container {
display: grid;
grid-template-columns: repeat(2, 100px 40px 50px);
grid-template-rows: 50px 50px 50px;
}
定义了100px 20px 80px 100px 20px 80px,6 列宽度不一的列。

如上所示,第三行由于没有item,默认空白。
某些场合下,我们希望容器尽可能填充每一行的item,可以使用auto-fill关键字:
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
}

容器根据最大宽度进行自动列填充,此时行与列的数量是根据宽度变化的。
某些场合下,我们希望动态根据片段比例对行数进行判断,次数可以使用fr(fraction)关键字,表示列的宽度片段,例如:
.container {
display: grid;
grid-template-columns: 2fr 1fr;
}
上述示例表示,第一列宽度为整个容器宽度的2/3,第二列为1/3,一般配合绝对宽度使用可以实现很灵活的布局效果:
.container {
display: grid;
grid-template-columns: 150px 1fr 2fr;
}
上述示例,每一行先扣除第一列的150px宽度,剩下的再动态计算分配。也可以使用auto关键字,由浏览器决定长度。
grid-template-columns: 100px auto 100px;
网格线可以具有名字,并且可以有多个名字(使用中括号括起来),方便后续复用。
.container {
display: grid;
grid-template-columns: [c1] 100px [c2] 100px [c3] auto [c4];
grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];
}
网格线间距属性gap,其属性为行和列的简写:
.container {
gap: <row-gap> <column-gap>;
gap: 20px 20px;
}
如果简写忽略了第二个值,则默认等于第一个值。
网格布局可以通过字符串,抽象画的划分不同item所属的区域。
.container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
grid-template-areas:
"a b c"
"d e f"
"g h i";
}
.area-a {
grid-area: a;
....;
}
grid-template-areas通过空格将不同区域分割开来,然后可以在css中直接使用grid-area属性和区域名作为值,再为标签添加类即可针对性的设置样式。
不使用的区域可以使用.占位,可以不同cell具有相同的area名,以便于指定样式,例如:
grid-template-areas:
"a . a"
"b . b"
"c . d";
grip-template-rows可以定义子项高度,同时也可以为网格线命名,而网格线可以有多个名字。grid-template-areas指定区域名的时候,也默认生成了areaName-start和areaName-end这样的网格线别名。
容器划分好网格后,容器内item按顺序放置,默认先行后列,这个顺序是可以更改的。grid-auto-flow就是设置这个顺序的属性,默认值为row,先行后列,如果需要先列后行,则设置值为column。
当某行或者某列按次序放置子项的时候,存在剩余宽度不足的情形,如果需要可以在row或column后添加一个dense值,二者用空格分开,意为尽可能让子项连续密集显示,如此一来就会跳过宽度超过剩余宽度的子项,按序优先使用后续满足条件的子项。例如:

属性:
grid-auto-flow: row dense;
结果:

对于某些严格需要避免中间空白的布局来说,这个属性非常有效。
justify-items设置单元格水平布局,align-items设置单元格垂直布局,二者可选的值为:
如果要设置整个容器内的单元格位置,也就是将容器内所有单元格视为一个整体,其布局属性可用:
这几个布局属性的值类似flex的布局值,分别是:
当容器网格只有三行的时候,如果需要指定某个子项在第五行,这时候浏览器自动根据子项大小创建新的网格以放置额外的子项,我们可以通过grid-auto-rows和grid-auto-columns指定自动创建的网格的高度和宽度。
例如:
.container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
grid-auto-rows: 50px;
}

之所以8和9会占据图中的位置,是因为我们使用css指定其行和列的位置值:
.item-8 {
background-color: #d0e4a9;
grid-row-start: 4;
grid-column-start: 2;
}
.item-9 {
background-color: #4dc7ec;
grid-row-start: 5;
grid-column-start: 3;
}
由此引出grid-row-start和grid-column-start属性,可以指定其元素的位置。
除了start还有end可以指定,看示例:
.item-1 {
grid-column-start: 2;
grid-column-end: 4;
}
此时如果没有指定grid-auto-flow: row dense;,则会让布局看起来如下图所示:

为了方便记忆,可以将网格线数字改为网格线名。
这四个属性的值还可以使用span关键字,表示"跨越",即左右边框(上下边框)之间跨越多少个网格。
.item-1 {
grid-column-start: span 2;
}

此前我有翻译过google html & css guide文档风格指南,其中有一条建议是尽量在css中使用简写,我认为这是一个很好的准则。
grid-template属性是grid-template-columns、grid-template-rows和grid-template-areas这三个属性的合并简写形式。
grid属性是grid-template-rows、grid-template-columns、grid-template-areas、 grid-auto-rows、grid-auto-columns、grid-auto-flow这六个属性的合并简写形式。
如果你喜欢简写,务必不要弄错简写的属性顺序。
grid-column属性是grid-column-start和grid-column-end的合并简写形式,grid-row属性是grid-row-start属性和grid-row-end的合并简写形式。
.item {
grid-column: <start-line> / <end-line>;
grid-row: <start-line> / <end-line>;
}
下面是一个例子。
.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;
}
上面代码中,项目item-1占据第一行,从第一根列线到第三根列线。
这两个属性之中,也可以使用span关键字,表示跨越多少个网格。
.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;
}
上面代码中,项目item-1占据的区域,包括第一行 + 第二行、第一列 + 第二列。

grid-area属性还可用作grid-row-start、grid-column-start、grid-row-end、grid-column-end的合并简写形式,直接指定项目的位置。
.item {
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
}
.item-1 {
grid-area: 1 / 1 / 3 / 3;
}
效果如上图所示。
子项和容器的属性可以拆分开来,通过诸如justify-self等带self关键字的属性控制单独的子项的样式,并且优先级高于容器上相关的样式属性。
justify-self属性设置单元格内容的水平位置(左中右),跟justify-items属性的用法完全一致,但只作用于单个项目。
align-self属性设置单元格内容的垂直位置(上中下),跟align-items属性的用法完全一致,也是只作用于单个项目。
.item {
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
}
place-self属性是align-self属性和justify-self属性的合并简写形式。
place-self: center center;
如果省略第二个值,place-self属性会认为这两个值相等。
学习了grid布局的知识后,让我们将之运用到一开头我的需求中来,再次看这个图:

针对性的容器CSS如下:
.container {
}