CSS网格可以定义由行和列组成的二维布局,然后将元素放置到网格中。有些元素可能只占据网格的一个单元,另一些元素则可能占据多行或多列。网格的大小既可以精确定义,也可以根据自身内容自动计算。你既可以将元素精确地放置到网格某个位置,也可以让其在网格内自动定位,填充划分好的区域。
跟 Flexbox
类似,网格布局也是作用于两级的 DOM
结构。设置为 display: grid
的元素成为一个网格容器(grid container)。它的子元素则变成网格元素(grid items)。
基础网页布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.grid{
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
grid-gap: 0.5em;
}
.grid > * {
background: springgreen;
color: white;
padding: 2em;
border-radius: 0.5em;
}
</style>
</head>
<body>
<div class="grid">
<div class="a">a</div>
<div class="b">b</div>
<div class="c">c</div>
<div class="d">d</div>
<div class="e">e</div>
<div class="f">f</div>
</div>
</body>
</html>
display:grid
使用 display: grid
定义一个网格容器。容器会表现得像一个块级元素,100%填充可用宽度。
也可以使用 inline-grid
(尽管这段代码没写),这样元素就会在行内流动,且宽度只能够包含子元素,不过 inline-grid
的使用频率不高。
grid-template-columns
和 grid-template-rows
grid-template-columns
定义列; grid-template-rows
定义行
两个属性定义了网格每行每列的大小。本例使用了一种新单位 fr
,代表每一列(或每一行)的分数单位(fraction unit)。这个单位跟Flexbox
中flex-grow
因子的表现一样。grid-template-columns:1fr 1fr 1fr
表示三列等宽。
不一定非得用分数单位,可以使用其他的单位,比如 px、em
或百分数。也可以混搭这几种单位,例如,grid-template-columns: 300px 1fr
定义了一个固定宽度为 300px
的列,后面跟着一个会填满剩余可用空间的列。2fr
的列宽是 1fr
的两倍。
其他使用方式
grid-template-rows: repeat(4, auto)
;定义了四个水平网格轨道,高度为auto,这等价于grid-template-rows: auto auto auto auto
。轨道大小设置为auto,轨道会根据自身内容扩展。
用repeat()
符号还可以定义不同的重复模式,比如repeat(3, 2fr 1fr)
会重复三遍这个模式,从而定义六个网格轨道,重复的结果是2fr 1fr 2fr 1fr 2fr 1fr
。
还可以将repeat()
作为一个更长的模式的一部分。比如 grid-template-columns: 1fr repeat(3, 3fr) 1fr
定义了一个1fr
的列,接着是三个3fr
的列,最后还有一个1fr
的列(可以用1fr 3fr 3fr 3fr 1fr
表示)。可以看出来因为展开的写法无法一目了然,所以才产生了repeat()
这种简写方式。
grid-gap
属性定义了每个网格单元之间的间距。也可以用两个值分别指定垂直和水平方向的间距。(比如grid-gap: 0.5em 1em
)。
网格剖析
基本概念
构建网格布局时会涉及这些组成部分。比如声明 grid-template-columns: 1fr 1fr 1fr
就会定义三个等宽且垂直的网格轨道,同时还定义了四条垂直的网格线:一条在网格最左边,两条在每个网格轨道之间,还有一条在最右边。
1、网格线
网格线(grid line)——网格线构成了网格的框架。一条网格线可以水平或垂直,也可以位于一行或一列的任意一侧。如果指定了grid-gap的话,它就位于网格线上。
2、网格轨道
网格轨道(grid track)——一个网格轨道是两条相邻网格线之间的空间。网格有水平轨道(行)和垂直轨道(列)。
3、网格单元
网格单元(grid cell)——网格上的单个空间,水平和垂直的网格轨道交叉重叠的部分。
4、网格区域
网格区域(grid area)——网格上的矩形区域,由一个到多个网格单元组成。该区域位于两条垂直网格线和两条水平网格线之间。
网格线编号
可以在grid-column
和grid-row
属性中用网格线的编号指定网格元素的位置。
如果想要一个网格元素在垂直方向上跨越1号网格线到3号网格线,就需要给元素设置grid-column: 1 / 3
。
或者设置grid-row: 3 / 5
让元素在水平方向上跨越3号网格线到5号网格线。
这两个属性一起就能指定一个元素应该放置的网格区域。
grid-column
和grid-row
grid-column
是 grid-column-start
和 grid-column-end
的简写;
grid-row
是 grid-row-start
和 grid-row-end
的简写。
中间的斜线只在简写属性里用于区分两个值,斜线前后的空格不作要求。
.box {
gird-column: 1 / 3;
grid-row: span 1;
}
其实还可以用一个特别的关键字 span
来指定 grid-row
和 grid-column
的值。这个关键字告诉浏览器元素需要占据一个网格轨道。因为这里没有指出具体是哪一行,所以会根据网格元素的布局算法(placement algorithm)自动将其放到合适的位置。布局算法会将元素放在网格上可以容纳该元素的第一处可用空间。
替代语法
布局网格元素还有另外两个替代语法:命名的网格线和命名的网格区域。至于选择哪个纯属个人偏好。
命名的网格线
有时候记录所有网格线的编号实在太麻烦了,尤其是在处理很多网格轨道时。为了能简单点,可以给网格线命名,并在布局时使用网格线的名称而不是编号。声明网格轨道时,可以在中括号内写上网格线的名称,如下代码片段所示。
grid-template-columns: [start] 2fr [center] 1fr [end]
这条声明定义了两列的网格,三条垂直的网格线分别叫作start、center和end。
之后定义网格元素在网格中的位置时,可以不用编号而是用这些名称来声明,如下代码所示。
grid-column: start / end ;
这条声明将网格元素放在1号网格线(start)到2号网格线(center)之间的区域。
还可以给同一个网格线提供多个名称,比如下面的声明(为了可读性,这里将代码换行了)。
grid-template-columns: [left-start] 2fr
[left-end right-start] 1fr
[right-end]
在这条声明里,2号网格线既叫作left-end
也叫作right-start
,之后可以任选一个名称使用。
这里还有一个彩蛋:将网格线命名为left-start
和left-end
,就定义了一个叫作left
的区域,这个区域覆盖两个网格线之间的区域。-start
和-end
后缀作为关键字,定义了两者之间的区域。如果给元素设置grid-column: left
,它就会跨越从left-start
到left-end
的区域。
命名的网格区域
另一个方式是命名网格区域。不用计算或者命名网格线,直接用命名的网格区域将元素定位到网格中。实现这一方法需要借助网格容器的grid-template
属性和网格元素的grid-area
属性。
grid-template-areas
属性使用了一种ASCII art
的语法,可以直接在CSS
中画一个可视化的网格形象。该声明给出了一系列加引号字符串,每一个字符串代表网格的一行,字符串内用空格区分每一列。
grid-template-areas: "a a" "b b" "c d" "c e"
使用时制定区域即可,grid-area: c
警告 每个命名的网格区域必须组成一个矩形。不能创造更复杂的形状,比如L或者U型。
还可以用句点(.)作为名称,这样便能空出一个网格单元。
实现开头图片的例子
方法一:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.grid{
display: grid;
/* grid-template-columns: 1fr 1fr 1fr 1fr 1fr; */
grid-template-columns: repeat(5, 1fr);
/* grid-template-rows: 1fr 1fr 1fr; */
grid-template-rows: repeat(3, 1fr);
grid-gap: 1em;
margin: 3em;
}
.grid > * {
background: rgba(0, 0, 0, 0.6);
color: white;
padding: 2em;
border-radius: 0.5em;
font-size: 2em;
}
.grid .d{
grid-column: 4 / 6;
grid-row: 1 / 3;
}
.grid .f {
grid-column: 2 / 4;
grid-row: 2 / 4;
}
</style>
</head>
<body>
<div class="grid">
<div class="a">1</div>
<div class="b">2</div>
<div class="c">3</div>
<div class="d">4</div>
<div class="e">5</div>
<div class="f">6</div>
<div class="g">7</div>
<div class="h">8</div>
<div class="i">9</div>
</div>
</body>
</html>
方法二:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.grid{
display: grid;
grid-template-areas: "a b c d d"
"e f f d d"
"g f f h i";
grid-gap: 1em;
margin: 3em;
}
.grid > * {
background: rgba(0, 0, 0, 0.6);
color: white;
padding: 2em;
border-radius: 0.5em;
font-size: 2em;
}
.grid .d{
grid-area: d;
}
.grid .f {
grid-area: f;
}
</style>
</head>
<body>
<div class="grid">
<div class="a">1</div>
<div class="b">2</div>
<div class="c">3</div>
<div class="d">4</div>
<div class="e">5</div>
<div class="f">6</div>
<div class="g">7</div>
<div class="h">8</div>
<div class="i">9</div>
</div>
</body>
</html>