网格布局(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 {
}