Web编程技术营地
研究、演示、创新

CSS Grid

撰写时间:2023-05-29

修订时间:2025-12-10

CSS Grid是极富创造性的一种页面布局编排机制,是CSS不断进化过程中的集大成者。它丢弃了HTML中table标签的呆板机制,却紧密地结合CSS各种最新技术,从CSS的角度,重新实现了table的功能,并有过之而无不及。

CSS Grid的内容较多,学习起来有一定的难度,本教程通过丰富的实例,并借助于本站自行开发的强大的LiveEditor的实时运行功能,清晰地演示了CSS Grid的各项关键技术,能较为有力地帮助我们系统学习与掌握CSS Grid技术。

本教程覆盖了CSS Grid 1.0版本的内容。

本教程使用下面的CSS通用设置:

div.grid-container { background: repeating-linear-gradient(45deg, #333333 0, #333333 5%, #4f4f4f 0, #4f4f4f 50%) 0 / 10px 10px; > div { border: 1px solid gray; padding: 0.5em; background-color: #333; } }

这样可更清楚地看到CSS Grid的效果。

如果需要在某个LiveEditor中改变这些默认值,则根据CSS的规则,可在LiveEditor的CSS标签页中添加诸如下面的代码:

body div.grid-container div { padding: 1em; }

因为使用了更长、更具体的选择器,因此上面的CSS设置将比通用设置有更高的优先权限。

Grid父容器的设置

div.grid-container { border: 1px solid deepskyblue; display: grid; grid-template-columns: 100px 1fr; }
A
B
C
D
E

观察HTML标签页中的内容,父容器共有5个一级子元素。由于这些子元素是块级元素,默认情况下,它们将各占一行,依序从上到下排列。

现在,将CSS标签页中的内容全部删除,点击Run按钮,可看到这种默认的排版情况。之后,重新刷新页面,恢复CSS标签页中的初始设置内容。

应用CSS Grid的第一步,是将父容器div的display属性设置为grid,这样,该div将成为Grid容器(上面以橙色边框标示)。Grid容器下面的所有第一级子元素将由CSS Grid统一进行管理。CSS Grid的本质在于智能地、灵活地指定Grid容器下各个一级子元素的行、列位置。

第二步,设置grid-template-columns属性值。取名为template-columns的含义为,在决定每一行中各个子元素应位于第几列时,均按此模板来设置。上面,该属性值被设定为100px 1fr,意为每行只排2个子元素,第1个子元素的宽度为固定的100px,第2个子元素的宽度属性值为1fr,则会占据该行中所有未使用的空间(这种空间,我们可称之为可用的弹性空间)。

排完第一行的元素后,还剩3个子元素。因为第一行已经没有空间,因此这3个子元素继续依照grid-template-columns: 100px 1fr的设置,依序在第2行、第3行中编排。第3行因为只有1个固定宽度的元素,因此右边还剩余许多弹性空间,其背景是斜纹灰底。

从左到右,依照模板格式编排子元素的各列,这是CSS Grid的最原始的设计理念。

练习:在LiveEditor中将grid-template-columns属性值设为50px 1fr 100px 25px,然后运行代码。查看结果,体会各种属性值的作用。

gap

可使用gap来设置Grid容器下的各个一级子元素相互之间的边距。

div.grid-container { border: 1px solid deepskyblue; display: grid; grid-template-columns: 100px 1fr; gap: 0.5em; }
A
B
C
D
E

乍看之下,其效果好像是为Grid容器下的各个一级子元素设置了margin值,实际上并不是。下面是设置margin值的效果。

div.grid-container { border: 1px solid deepskyblue; display: grid; grid-template-columns: 100px 1fr; > div { margin: 0.5em; } }
A
B
C
D
E

对比之下可以看出,gap只设置子元素之间的边距,但不设置子元素与父容器之间的边距。

grid-template-columns

正如上面所见,CSS Grid在Grid容器内按行、列的形式来定位各个子元素。

先按行、再按列来定位,还是先按列、再按行来定位,将产生不同的效果。grid-auto-flow属性决定使用哪种定位机制,当其值为row时,将先按行、再按列定位;当其值为column时,将先按列、再按行定位。默认值为row,因此,默认情况下先按行、再按列定位。

grid-auto-flow下面再详谈。现在我们先关注于在先按行、再按列定位的默认情况下,如何排列各列。

先按行、再按列来定位的具体细节是,将Grid容器下的各个一级子元素依序先排满第1行,当第1行排满后,将剩余的子元素依序排在第2行,依此类推。

显然,我们需要告诉CSS Grid,每行应排多少列的子元素。grid-template-columns目的正在于此,它显式地指定了各列的宽度。

显式与隐式的区别

div.grid-container { display: grid; grid-template-columns: 150px 50px; } div#b { grid-row: 1; grid-column: 3; background-color: #444; }
A
B
C
D
E

声明了2个模板列,因而依此模板列的默认情况下,各个子元素将按每行2列的模板依序定位,直至结束。

但上面,我们还特意将B元素额外定位于第1行的第3列,由于它不在模板列范围内,CSS Grid将隐式地为其创建第3列。

排版顺序是,先定位用户自行指定位置的B元素,其余子元素则按模板列格式来定位。

能生成显式轨道 (explicit grid,这里中文使用显式轨道以指代) 并提供具体规范条件的属性有:grid-template-rows, grid-template-columns及grid-template-areas。

对于CSS Grid所自动创建的隐式轨道 (implicit grid), 可通过grid-auto-rows及grid-auto-columns来规范。

轨道列表

grid-template-columns的值可为一系列的轨道列表 (track-list),轨道之间以空格符隔开。

在轨道列表的值中,可指定一系列的轨道尺寸 (track-size),或使用reapeat函数来指定需重复的轨道 (track-repeat)。

下面以轨道尺寸的方式来指定轨道列表值。

div.grid-container { display: grid; grid-auto-flow: row; grid-template-columns: 1fr 2fr 30px 3fr; }
A
B
C
D
E

轨道尺寸的语法为:

<track-size> = <track-breadth> | minmax( <inflexible-breadth> , <track-breadth> ) | fit-content( <length-percentage [0,∞]> )

下面各节先依序考查轨道尺寸的各种值,再考查如何以轨道重复的方式来指定值。

fr的作用

fr有两种格式:一是诸如3fr(前面的数值大于等于1)的格式;二是诸如0.3fr(前面的数值小于1的格式)。下面分别考察。

数值大于等于1的fr

div.grid-container { display: grid; grid-template-columns: 1fr 2fr 30px 3fr; }
A
B
C
D
E

fr是可自动变长的数值。上面代码,grid-template-columns属性值为1fr 2fr 30px 3fr,则CSS Grid先在该行中优先确定C元素的宽度,然后,将该行所剩的空间在其他几个具有fr属性值的子元素之间再进行分配。

fr意为部分,数值为百分比的形式,我们也可以理解为单位。上面的例子中,第1个子元素占1个单位的宽度,第2个子元素占2个单位的宽度,第4个子元素占3个单位的宽度。因此,若说A的宽度是1个单位,则B的宽度是A的2倍即2个单位,D是A的3倍,即3个单位。

1fr确定了分子为1,分母呢?分母为该行中所有具有fr属性值的子元素的数值总和。因此,上面分母为1 + 2 + 3 = 6,则A元素宽度为总宽度除去C元素宽度之外的1/6,B元素宽度为2/6,D元素宽度为3/6。如果一行只有一个具有fr属性值的子元素,则不管其数值是多少,都会占去所有的弹性空间。因此,它与n%的算法不一样。

我们可以这样思考。先确定一个1fr的元素,然后在剩下的空间中,按照其他元素是该元素的几倍来确定。如A元素的宽度为1fr,如果B元素应是A元素的2倍,则取2fr。余此类推。

数值小于1的fr

grid-template-columns的值中可以使用0.1fr的形式,即fr的前面是小于1的小数。这种方式类似于n%的效果。下面看各个小数之和的情况。

和等于1

在理想的情况下,一行中各个元素小数点之和应等于1。

div.grid-container { display: grid; grid-template-columns: 0.2fr 0.8fr; }
A
B
C
D
E

此时,各元素能自动铺满所在行的可用弹性空间。

和小于1

div.grid-container { display: grid; grid-template-columns: 0.2fr 0.6fr; }
A
B
C
D
E

此时,各元素按指定的比例占用所在行的可用弹性空间,但留下了0.2的可用弹性空间未使用。

既然仍有可用弹性空间,如果再加上一个固定宽度的元素呢?

div.grid-container { display: grid; grid-template-columns: 0.2fr 0.6fr 100px; }
A
B
C
D
E

其结果是,先保证固定宽度的C元素的宽度,然后再将剩余的可用弹性空间按0.2、0.6、0.2的比例依序分配给A元素及B元素,由于各元素小数点之和未达到1,因此仍留下0.2的可用弹性空间。

和大于1

div.grid-container { display: grid; grid-template-columns: 0.9fr 0.3fr; }
A
B
C
D
E

由于总和超过1,此时,两个元素将占满所在行的可用弹性空间,且保持之间的比例0.9:0.3。

这个效果,与grid-template-columns: 9fr 3fr;的效果完全一致。

因此,当出现两个超大的小数点,且这两个小数点的值又一样时,如:grid-template-columns: 0.9fr 0.9fr;,当您看到它们两个在如下所示的渲染时表现为平分可用弹性空间,就不会感到奇怪了。

div.grid-container { display: grid; grid-template-columns: 0.9fr 0.9fr; }
A
B
C
D
E

auto

auto也是一个会自动变长的属性值。但,与fr不同的是,auto有较多的细节需要注意。

单独使用auto

div.grid-container { display: grid; grid-template-columns: auto; }
A
B
C
D
E

当每一行只有一列,且其值为auto时,该列会自动铺满可用弹性空间。

div.grid-container { display: grid; grid-template-columns: auto auto auto; }
A
B
C
D
E

当一行有多个其值均为auto的列时,以每列宽度都相等的方式自动铺满可用弹性空间。

auto与固定宽度值共用

div.grid-container { display: grid; grid-template-columns: auto 100px auto; }
A
B
C
D
E

当auto与固定宽度值共用时,所有auto列会自动按比例铺满剩余的可用弹性空间。此时auto相当于1fr的效果。

auto与fr共用

div.grid-container { display: grid; grid-template-columns: auto 2fr 2fr; }
A
B
C
D
E

当auto与fr共用时,auto降格了,不再有弹性拉伸的作用,仅保留最小宽度,将其拉伸的优先权让位于fr,由fr来铺满剩余的可用弹性空间。

minmax

div.grid-container { display: grid; grid-template-columns: minmax(400px, 450px) 50px; }
A
B
C
D
E

minmax函数允许特定列在值域内取值。第1列的宽度最小值为400px,最大值为450px,第2列是固定的50px。

如果您是在桌面电脑上访问此页面,则由于浏览器客户端有许多空间,而A元素最大值只有450px,因此每行会留出许多空白,透出我们特意设定的斜纹灰底(该部分为CSS Grid所管理的空间)。如果拉动浏览器边框以改变其宽度,当宽度越来小时,CSS Grid会优先使用多出的斜纹灰底空间,这样斜纹灰底空间会越来越小,从而确保尽可能使A元素获得最大值。而一旦多余空间耗尽,则开始减少A元素的宽度,但确保其宽度至少为400px。当继续减少浏览器的宽度,已经不能满足A元素与B都同时出现在可视区域内时,如果父容器允许,则出现水平滚动条,从而创建出更多的空间。

一句话,如果空间足够,则优先保障该元素的最大值;如果没有空间,至少保障该元素的最小值。

举个例子,我们对某人说,如果空间充裕,你也不要占得太多,得留出一些空间给别人;但如果空间太小,我可以保证你至少获得100平米的空间。

理解并掌握这个属性值,在分别为桌面及移动客户端布局时非常有用。我们可以为某个核心元素先设定一个必需的最小值,然后再设定一个合理的最大值。这样就不会因为空间大幅改变后而导致冒然地失去应用功能。

练习:在LiveEditor中将grid-template-columns属性值设为minmax(100px, 1fr) 50px;。这种布局,可让A列吃掉所有多余的空间,但又确保最小值。

还有专门的min函数及max函数,语义上更容易理解了。

min-content, max-content, fit-content

这3个属性值涉及到父容器如何包裹子元素的问题。

八卦中以五行生克来定吉凶。五行生克可以很简单,但蕴涵了许多朴素而深奥的哲学原理。

#wrapper { border: 1px solid yellow; } @media (prefers-color-scheme: dark) { img { filter: invert(100%); } }

外层div包裹了两个子元素,一个是img,另一个是p,它们都是块级元素。由于外层div同时也是块级元素,因此它独立占了一整行,从而导致图像及段落的右边都留下了不好看的空间。现在,我们的目标是,如何让外层容器自动紧密地包裹这些内部子元素?

设置外层容器的width属性值为max-content:

八卦中以五行生克来定吉凶。五行生克可以很简单,但蕴涵了许多朴素而深奥的哲学原理。

#wrapper { border: 1px solid yellow; width: max-content; } @media (prefers-color-scheme: dark) { img { filter: invert(100%); } }

外层容器的宽度值取自于两个子元素中最大的宽度值,即段落的宽度值。与上面的结果相比较,段落右边是没有空间了,但图像右边还有,照样不好看。

设置外层容器的width属性值为min-content:

八卦中以五行生克来定吉凶。五行生克可以很简单,但蕴涵了许多朴素而深奥的哲学原理。

#wrapper { border: 1px solid yellow; width: min-content; } @media (prefers-color-scheme: dark) { img { filter: invert(100%); } }

这时,外层容器的宽度值取自于两个子元素中最小的宽度值。两个子元素虽为块级元素,但它们还是存在区别的:图像的宽度不能自动改变,而段落的宽度则可以通过断行而自动改变。因此,此时则取图像的宽度作为外层容器的宽度值即可。

fit-content也是一个CSS属性名。它的意义在于,外层容器所使用的空间,不会大于max-content。因此,它的效果与max-content一样。

需注意的是,同时存在fit-content()的CSS函数。

repeat函数

当多列的数值出现多次重复时,可使用repeat函数。

repeat(n, unit)

div.grid-container { display: grid; grid-template-columns: repeat(3, 80px); }
A
B
C
D
E

意为,重复3次80px的宽度,重复次数放在前面,单位放在后面。因此,它与下面的代码是等效的:

div.grid-container { display: grid; grid-template-columns: 80px 80px 80px; }

repeat(n, unit unit)

div.grid-container { display: grid; grid-template-columns: repeat(2, 80px 50px); }
A
B
C
D
E

上面,需重复2次,且每次需要重复的宽度值为:80px 50px,则可使用repeat(2, 80px 50px)的形式。

repeat auto-fill

repeat(auto-fill, unit)

div.grid-container { display: grid; grid-template-columns: repeat(auto-fill, 150px); }
A
B
C
D
E

上面的repeat(n, unit...)先确定每一列的具体宽度,再指定了每一行应有多少个这种宽度的列。因此,不管父容器的宽度是多少,每一行的列数是固定的。当该行没有空间而排不下这些元素时,则通过在父容器显示水平滚动条的方式来添加空间。

但我们往往还有另外一种需求,即,如果水平空间够宽敞,但希望排3个子元素,如果水平空间不够,则只需排2个子元素就行了。显然,这里子元素的个数是不确定的,因此不能使用上面的格式。repeat(auto-fill, unit)可用以解决这种特定的需求。

repeat(auto-fill, unit)含义是,一次排一个宽度为unit的元素,再看剩下的空间能否排得下第二个这种宽度的元素。如果剩下的空间排得下第二个,则将第二个元素排在第一个元素的右边。然后再看第三个、第四个, ...,依此类推。若遇到剩下的空间不足以排得下新的元素,则将新的元素排在第二行。在新行中,再重复上面的算法。

这样,只要我们指定的列宽,CSS Grid就会根据可用空间来自动安排在每行上的元素个数,从而既保证了列宽,又能在有限的空间内尽可能多地排上各个元素。

repeat(auto-fill, minmax)

上面我们指定每个元素为固定的150px的宽度,因此会造成每行右边会有留白。如果希望各元素还能自动铺满一行,则可以将每列的宽度指定minmax(150px, 1fr):

div.grid-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
A
B
C
D
E

这样,每列宽度不会少于150px,并且当每行右边的留白又不足以排下一个新元素时,则会平等的拉伸每个现有元素的宽度,以自动铺满整行的空间。

它的效果为:

  1. 每一列的宽度值最小为150px。
  2. 至于每行能排多少列,由CSS Grid帮我们自动计算。
  3. 当每行上剩有多余的弹性空间时,将同时等量地拉伸各列以铺满该行。

这样,在保证每列最小宽度值的同时,由CSS Grid自动帮我们计算列数,又能让每行不留空白。在许多场合下,我们确实需要此功能。

然而,这里有一个小问题:如果每列的宽度太小,即使都排完了所有元素,auto-fill也不会自动铺满整行。如下所示:

div.grid-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)); }
A
B
C
D
E

repeat auto-fit

auto-fit可以解决上一节中的问题,即使每列的宽度太小而有留白,这些留白的宽度将变为0px,也即产生了塌陷(collapse),从而导致auto-fit可以自动拉伸每个元素的宽度以铺满整行。

div.grid-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); }
A
B
C
D
E

综上,如果需要拉伸效果,而又无需考虑元素的最小宽度值是否过小,则应使用repeat(auto-fit, minmax())的代码。

grid-template-rows

grid-template-rows设定各行高度的模板。

未指定grid-template-columns及grid-template-rows

当既未指定grid-template-columns,也未指定grid-template-rows时,排版效果是每行一个铺满整行的元素。

div.grid-container { display: grid; > span { border: 1px solid gray; padding: 0.5em; background-color: #333; } }
A B C D E

注意,尽管上面每个子元素都是行内元素span,因由CSS Grid管理而具有块级元素的特征。

指定行数正好等于子元素数量

div.grid-container { display: grid; grid-template-rows: 1fr auto 2fr 40px 50px; }
A
B
C
D
E

指定模板行的数量少于总行数量的情况

div.grid-container { display: grid; grid-template-columns: 50px; grid-template-rows: 15px 15px 15px; }
A
B
C
D
E

上面子元素共有5行,而我们仅指定了3行,则第4行、第5行的高度取相应子元素的初始值。

我们使用grid-template-columns指定模板列时,如果模板列数少于所有子元素的数量,则会自动产生断行,新行中的子元素继续依照模板列的格式来编排。而grid-template-rows不会重复使用模板行的设置。如果我们指定的模板行不包含特定行,则特定行取默认高度值。

在许多情况下,总行的数量并非总是可以确定下来。那么如何自动涵盖所有的行?

使用grid-auto-rows为非模板行设置高度

grid-template-columns应对模板行,对于非模板行,可以使用grid-auto-rows来应对。

div.grid-container { display: grid; grid-template-columns: 50px; grid-template-rows: 50px 20px 20px; grid-auto-rows: 80px; }
A
B
C
D
E

上面模板行只指定了3行,则最后2行为非模板行,通过设定grid-auto-rows: 80px,我们将所有这些行的高度统一设置为80px。

使用repeat来遍历所有的行

grid-template-rows可以使用repeat函数来遍历所有的行。

div.grid-container { display: grid; grid-template-columns: 50px; grid-template-rows: repeat(10, min-content); }
A
B
C
D
E

但这种方式,与不设置grid-template-rows一样。如果我们改为grid-template-rows: repeat(10, 50px);,则会看到,比总行数量多出的设置,会导致在下面产生了不必要的空间。这种方式不可取。

因此,还是像上节一样,使用grid-auto-rows: 80px;的方式更为稳妥。

CSS Grid布局术语详解

CSS Grid有多种精准定准子元素的方法,我们需要先了解CSS Grid相关术语,才能做到深刻理解随心所欲地予以编排。CSS Grid的结构示意图如下。

grid-lines

根据上面所学的知识,我们可以精准地用代码复现此示意图,并在相应的位置放置子元素。

div.grid-container { display: grid; grid-template-columns: 150px 1fr; grid-template-rows: 50px 1fr 50px; min-height: 250px; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

共有A, B, C, D, E, F等6个子元素,每列排2个元素,故共有3行。

最外面的4条虚线所围成的区域称为grid,它用以整齐地排列各个子元素。图中的grid共有3行2列。

先看示意图中列的情况。用3条垂直竖线将每行分割为2列。示意图中,第1条垂直竖线用line 1表示,第2条垂直竖线用line 2表示,第3条垂直竖线用line 3表示。

同理,示意图使用4条水平横线将整个grid分割为3行。从上到下,其名称分别为line 1, line 2, line 3, line 4。

对于这些分割行或列的虚线,我们统称为grid line。两条相邻的同向的grid line之间区域称为grid track。因此,它是一行或一列的统称。例如,在水平方向上,A与B在同一grid track上,而在垂直方向上,A, C, E在同一grid track上。

这些grid lines将整个grid划分为6个区域,用于分别放置A, B, C, D, E, F等6个子元素。我们将这些可以放置子元素的最小区域称为grid cell。在上面的例子中,A, B, C, D, E, F均独立占具一个grid cell。

若干个相邻的grid cell可以组成一个更大空间的grid area。搬用电子表格中的用语,grid area就是指若干个相邻的grid cell合并后的合并单元格。

Grid line的定义与引用

grid-row-start, grid-row-end, grid-column-start, 以及grid-column-end这4个属性需要引用具体的grid-line。

使用Grid line的序号

每条Grid line都有自己的序号。从左到右及从上到下,序号从1开始,逐渐增大。也可以使用负数来表示倒数的序号,-1表示倒数第1行或倒数第1列,-2表示倒数第2行或倒数第2列,依此类推。Chrome, Safari, Firefox等浏览器的开发者工具可以查看CSS Grid的Grid line的序号。Chrome的效果如下图所示:

Grid line index

现在,我们准备将B排在1行1列,F排在2行2行。

div.grid-container { display: grid; grid-template-columns: 150px 1fr; grid-template-rows: 50px 1fr 50px; min-height: 250px; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: 1; grid-row-end: 2; grid-column-start: 1; grid-column-end: 2; } #f { grid-row-start: 2; grid-row-end: 3; grid-column-start: 2; grid-column-end: 3; }
A
B
C
D
E
F

第1行第1列这个grid cell所在的行由序号为1和2的两条grid line分割。因此,我们用grid-row-start: 1;来指定所在行从1这条grid line开始,用grid-row-end: 2;表示至2这条grid line结束。

同理,用grid-column-start: 1;来指定所在列从1这条grid line开始,用grid-column-end: 2;表示至2这条grid line结束。这样便达到精准定位的目标。

对于F元素而言,它的行始于第2条grid line,终于第3条grid line;它的列始于第2条grid line,终于第3条grid line。

我们也可以使用负数的序号为子元素定位。例如,F元素也可这样指定:

#f { grid-row-start: -3; grid-row-end: -2; grid-column-start: -2; grid-column-end: -1; }

上面,我们显式地指定了B与F元素的位置,CSS Grid将优先保证它们两个的位置,然后再将其他元素依照声明的次序,逐个排进模板列及模板行中。

为Grid line命名

除了直接引用grid line序号之外,我们还可以为grid line取名,在CSS Grid中称为custom ident,即custom identifier(自定义标识符)。

一般命名方法

div.grid-container { display: grid; grid-template-columns: [first-grid-line] 150px [second-grid-line] 1fr; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: 1; grid-row-end: 2; grid-column-start: first-grid-line; grid-column-end: second-grid-line; }
A
B
C
D
E
F

首先,在设置grid-template-columns或grid-template-rows时,在相应的单元格左右或上下的grid line出现的地方,加入相应的名称,名称用[ ]包围起来:

div.grid-container { ... grid-template-columns: [first-grid-line] 150px [second-grid-line] 1fr; ... }

其次,子元素引用这些grid line的名称,但去掉定义名称时所加上的[ ]:

#b { grid-row-start: 1; grid-row-end: 2; grid-column-start: first-grid-line; grid-column-end: second-grid-line; }

一般情况下,我们需要引用哪些grid line时,才需要为它们命名。不需要引用的,就没必要浪费时间给它们命名了。

grid-template-columns与grid-template-rows的grid line的名称可以重合,不用担心命名会起冲突,因此它们应用的语义环境不一样。

div.grid-container { display: grid; grid-template-columns: [first-grid-line] 150px [second-grid-line] 1fr; grid-template-rows: [first-grid-line] auto [second-grid-line] auto auto; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: first-grid-line; grid-row-end: second-grid-line; grid-column-start: first-grid-line; grid-column-end: second-grid-line; }
A
B
C
D
E
F

上面,我们直接从具体grid line的角度,将它们取名为第几条线,这当然比不上直接引用序号便捷,目的仅在于演示而已。当然,您可以根据自己的语义环境,为它们取更有意义的名称。

遵循约定的命名方法

现在,我们从子元素的角度,为围绕其的grid line命名。

div.grid-container { display: grid; grid-template-columns: [item-b-start] 150px [item-b-end] 1fr; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: 1; grid-row-end: 2; grid-column-start: item-b-start; grid-column-end: item-b-start; }
A
B
C
D
E
F

我们将排在B元素左右两边的两条grid line分别取名为item-b-start及item-b-end,表示这两条grid line分别是B元素的起止之线。这是以元素为核心的命名方法。

上面的命名方式以分别以-start及-end作为后缀命名。CSS Grid欢迎这种命名方式,并约定,若以这种方式命名,在引用该名称时,无需引用其后缀部分。

div.grid-container { display: grid; grid-template-columns: [b-start] 150px [b-end] 1fr; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: 1; grid-row-end: 2; grid-column-start: b; grid-column-end: b; }
A
B
C
D
E
F

上面,我们将围绕B元素的两条垂直grid line分别命名为b-start及b-end,而在引用时,只需指定为b即可。

#b { ... grid-column-start: b; grid-column-end: b; }

在查找时,CSS Grid会自动根据其所在的属性名称,自动为它们加上相应的后缀。

在下面的为grid area命名一节中,我们将会看到,CSS Grid就是根据这个特性添加了隐式的名称。

多条Grid line可以共享同一名称

div.grid-container { display: grid; grid-template-columns: [v-line] 150px [v-line] 1fr [v-line]; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: 1; grid-row-end: 2; grid-column-start: v-line; grid-column-end: v-line; }
A
B
C
D
E
F

上面,在声明时,3条垂直的grid line均使用同一名称v-line,且在引用时也均一样地引用了v-line。CSS Grid会自动地从左到右地提取对应的grid line。

如果我们希望改变CSS Grid的这种默认行为,例如,我们希望B元素从第1条grid line开始,至第3条grid line结束,则可以通过在名称后面添加[space] index的方式进行指定。index的值也可为负数。

div.grid-container { display: grid; grid-template-columns: [v-line] 150px [v-line] 1fr [v-line]; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: 1; grid-row-end: 2; grid-column-start: v-line 1; grid-column-end: v-line 3; }
A
B
C
D
E
F

一条Grid line可以有多个名称

同一条grid line可以有多个名称,各个名称之间用空格隔开。引用时,可使用该线条的任一名称。

div.grid-container { display: grid; grid-template-columns: [b] 150px [b a] 1fr [a]; } body div.grid-container div { border: 1px dashed gray; } #b { grid-row-start: 1; grid-row-end: 2; grid-column-start: b; grid-column-end: b; } #a { grid-column-start: a; grid-column-end: a; }
A
B
C
D
E
F

使用span来跨行或跨列

指定单数的起始行列,再指定复数的终止数量

div.grid-container { display: grid; grid-template-columns: 150px 1fr; } #a { grid-column-start: 1; grid-column-end: span 2; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

为grid-column-start指定一个起始列,再通过span n为grid-column-end设定一个跨度数值。

上面初始指定的列数为2列,如果指定span n后超过了指定的列数,则会隐式地添加多余的列。例如:

div.grid-container { display: grid; grid-template-columns: 150px 1fr; } #c { grid-column-start: 1; grid-column-end: span 3; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

C元素要求有3列的宽度,则模板列将变为3列。而之前的A, B元素先按默认的2列排列,在变为3列后,其所在行会有留白。

但如果C元素之前的A或B元素已经先排,则A或B元素所在行不会留白。

div.grid-container { display: grid; grid-template-columns: 150px 1fr; } #a { grid-row-start: 1; grid-row-end: 2; grid-column-start: 1; grid-column-end: 1; } #c { grid-row-start: 2; grid-row-end: 3; grid-column-start: 1; grid-column-end: span 3; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

grid-column-start跨列

div.grid-container { display: grid; grid-template-columns: 150px 1fr; } #a { grid-column-start: span 2; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

注意,不能在span的前面再引用一行或一列,例如:grid-column-start: 1 span 2;。这是错误的。

grid-column-end跨列至指定的列名

div.grid-container { display: grid; grid-template-columns: 150px 1fr [v-line]; } #a { grid-column-start: 1; grid-column-end: span v-line; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

注意,grid-column-start不支持这种设置值。

同名的跨域

跨至中间的名称:

div.grid-container { display: grid; grid-template-columns: [a] 150px 1fr [a] 1fr [a]; } #a { grid-column-start: a; grid-column-end: span a; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

跨至最后一个:

div.grid-container { display: grid; grid-template-columns: [a] 150px 1fr [a] 1fr [a]; } #a { grid-column-start: a; grid-column-end: a -1; } body div.grid-container div { border: 1px dashed gray; }
A
B
C
D
E
F

直接指定最后一列就行,不能使用span。

小结

使用grid-row-start这些属性名的不便之处在于,在指定grid line时,我们需要脑补这些grid line的序号。此外,细节比较繁杂。

grid-row及grid-column

grid-row指定元素所在的行,grid-column指定元素所在的列。

grid-row是grid-row-start及grid-row-end的简写。grid-column是grid-column-start及grid-column-end的简写。

基本用法

div.grid-container { display: grid; grid-template-columns: 150px 1fr; } #a { grid-row: 1; grid-column: 2; } #b { grid-row: 3; grid-column: 1; }
A
B
C
D
E
F

上面,我们将A子元素排在第1行第2列,将B子元素排在第3行第1列。代码简洁明了,非常直观。

跨行或跨列

div.grid-container { display: grid; grid-template-columns: 150px 1fr; } #a { grid-row: 1 / span 2; grid-column: 2; } #b { grid-row: 3; grid-column: 1 / span 2; }
A
B
C
D
E
F

对于A元素,使用grid-row: 1 / span 2;来指定所在行从第1行开始,跨2行。对于B元素,使用grid-column: 1 / span 2;来指定所在列从第1列开始,跨2列。

指定起止行列

div.grid-container { display: grid; grid-template-columns: 150px 1fr; } #a { grid-row: 1; grid-column: 2 / 4; }
A
B
C
D
E
F

A元素,使用grid-column: 2 / 4;来指定其单元格从第2条grid-line开始,到第4条grid-line结束。由于模板列只有2列,故会自动添加第3列至模板列中。

注意比较:

grid-column: 1 / span 2: 从第1列开始,跨2列。引用的是grid-cell。

grid-column: 1 / 2: 从第1条grid-line开始,至第2条grid-line结束。引用的是grid-line。

使用grid-template-areas为空间命名

基本语法

div.grid-container { display: grid; grid-template-areas: "header header header" "nav article aside" "footer footer footer"; grid-template-columns: auto 1fr 20%; gap: 0.2em; } #header { grid-area: header; } #nav { grid-area: nav; } #article { grid-area: article; } #aside { grid-area: aside; } #footer { grid-area: footer; }

Aritcle title

paragraph 1

parapgraph 2

Aside

第一步,先使用grid-template-areas将父容器划分为几大部分,并为它们相应命名。跨行或跨列的,重复上行或上列的名称即可。第二步,将各个子元素通过grid-area与所命名的区域对应起来。

区域名称并无太多限制,甚至可以使用单个英文字符。如果grid-area允许出现留白,则用单个或多个.来代替。

练习:将grid-template-areas的最后一行属性值改为... footer ...看看效果如何。

空间命名的便利之处

空间命名的一个便利之处是非常方便做整个版面的调整。

div.grid-container { display: grid; grid-template-areas: "header header header" "nav article aside" "footer footer footer"; grid-template-columns: auto 1fr 20%; gap: 0.2em; } #header { grid-area: header; } #nav { grid-area: nav; } #article { grid-area: article; } #aside { grid-area: aside; } #footer { grid-area: footer; } @media all and (max-width: 400px) { div.grid-container { grid-template-areas: "header" "article" "footer"; grid-template-columns: 1fr; } #nav, #aside { display: none; } }

Aritcle title

paragraph 1

parapgraph 2

Aside

上面的代码,在桌面电脑上浏览时,效果与上节一致。而当客户端宽度小于等于400px时,此为移动设备的宽度,则只显示主干区域,并将两个非主干区域隐藏起来。

grid-template属性名称

grid-template是grid-template-rows, grid-template-columns及grid-template-areas的简写。

基本用法

以行 / 列的格式来定义。

div.grid-container { display: grid; grid-template: 1fr 1fr 1fr / 150px 1fr; }
A
B
C
D
E
F

定义了3行及2列。其效果等同于:

div.grid-container { display: grid; grid-template-rows: 1fr 1fr 1fr; grid-template-columns: 150px 1fr; grid-template-areas: none; }
A
B
C
D
E
F

融入grid-template-areas

再进一步,将上面的行的定义改写为grid-template-areas的形式:

div.grid-container { display: grid; grid-template: "a a b" 1fr "c c c" 2fr "d e e" 1fr "d f f" 2fr / 50px 100px 50px; } #a {grid-area: a;} #b {grid-area: b;} #c {grid-area: c;} #d {grid-area: d;} #e {grid-area: e;} #f {grid-area: f;}
A
B
C
D
E
F

等同于:

div.grid-container { display: grid; grid-template-areas: "a a b" "c c c" "d e e" "d f f"; grid-template-rows: 1fr 2fr 1fr 2fr; grid-template-columns: 50px 100px 50px; } #a {grid-area: a;} #b {grid-area: b;} #c {grid-area: c;} #d {grid-area: d;} #e {grid-area: e;} #f {grid-area: f;}
A
B
C
D
E
F

相比之下,使用grid-template定义grid areas,然后将各行的高度分别列在各行名称的右边,反倒更为直观。

grid属性名称

grid是grid-template-rows, grid-template-columns, grid-template-areas, grid-auto-rows, grid-auto-columns, 以及grid-auto-flow的简写。

grid-auto-flow属性

grid-auto-flow属性决定先排行还是先排列。

如果属性值是默认值row,则先依据每行中所有列的数量,先排完一行的各列,再排另一行。

div.grid-container { display: grid; grid-template: repeat(4, 1fr) / repeat(2, 1fr); grid-auto-flow: row; }
A
B
C
D
E
F

先排行。每行中有2列,则A, B子元素先排在第1行,然后另起一行,继续排完后面的子元素。

而如果属性值是column,则先依据每列中各行的数量,先排完一列的各行,再排另一列。

div.grid-container { display: grid; grid-template: repeat(4, 1fr) / repeat(2, 1fr); grid-auto-flow: column; }
A
B
C
D
E
F

先排列,每列有4行,故A, B, C, D先排在第1列,然后再另起一列。

grid-auto-flow的属性值还可以是row dense或column dense。用于决定在前面已经排过的位置中如果出现留白,是否可以由后面的元素排在留白的位置。看下面的例子。

div.grid-container { display: grid; grid-template: repeat(4, 1fr) / repeat(2, 1fr); grid-auto-flow: row; } #a {grid-row-start: 3;} #d {grid-column-start: 2;}
A
B
C
D
E
F

优先按行到排列。我们指定A元素必须排在第3行,因此,B与C元素先排在第1行。接着到D元素。由于我们指定该元素必须在第2列,因此它排到了第2行的第2列,导致第2行的第1列留白。E, F元素则跟在A元素之后排列。

现在,将grid-auto-flow的属性值改为row dense。

div.grid-container { display: grid; grid-template: repeat(4, 1fr) / repeat(2, 1fr); grid-auto-flow: row dense; } #a {grid-row-start: 3;} #d {grid-column-start: 2;}
A
B
C
D
E
F

与上面不同的是,紧跟在A元素之后的E元素,看到D元素的前面有留白,就在该留白处予以排列。即,后面排列的元素自动填补前面的留白。

再看优先按列排列的情况。

div.grid-container { display: grid; grid-template: repeat(4, 1fr) / repeat(2, 1fr); grid-auto-flow: column; } #a {grid-row-start: 3;} #d {grid-column-start: 2;}
A
B
C
D
E
F

A先排在第1列的第3行,导致该列前面2行留白。到C时,已另起1列至第2列,此时由于D被指定为必须排在第2列,其优先权高于C,因此,先排D再排C。E及F排在第2列的最后。

将grid-auto-flow的属性值改为column dense。

div.grid-container { display: grid; grid-template: repeat(4, 1fr) / repeat(2, 1fr); grid-auto-flow: column dense; } #a {grid-row-start: 3;} #d {grid-column-start: 2;}
A
B
C
D
E
F

B与C则迫不及待地跳上去填补留白了。

grid属性的用法

div.grid-container { display: grid; grid: 1fr 1fr 1fr / auto-flow 150px 1fr; // 以列为先 }
A
B
C
D
E
F

很多时候,名称越短,功能更丰富,使用更简单,但在CSS中却相反:CSS的属性名称越短,其越难理解与掌握。下面是grid-area简写金字塔:

grid-area
grid-columngrid-row
grid-column-startgrid-column-endgrid-row-startgrid-row-end

下面是grid简写金字塔:

grid
grid-templategrid-auto-columnsgrid-auto-rowsgrid-auto-flow
grid-template-rowsgrid-template-columns

对齐

整个grid在grid container中的对齐

在grid container中使用justify-content使整个grid在grid container中水平对齐,在grid container中使用align-content使整个grid在grid container中垂直对齐。

div.grid-container { display: grid; grid-template-columns: 150px 250px; justify-content: center; align-content: center; min-height: 150px; }
A
B
C
D
E
F

上例中灰底部分为grid container所占用的空间,也即grid布局的空间。由于我们指定每行中的2列分别为150px及100px,因此不能占满整行,在水平方向上有留白。同样,每行的高度也均使用默认的固定值,3行总高度也不能占满整个grid container的高度上的空间,因此在垂直方向上也有留白。

当grid container有留白时,整个grid便可在其中上下左右移动。此时即可在grid container元素中使用justify-content及align-content来控制grid的偏移位置。

justify-content的值包括:normal, space-between, space-around, space-evenly, stretch, safe, unsafe, center, start, end, flex-start, flex-end, left, right.

align-content的值包括:normal, first, last, baseline, space-between, space-around, space-evenly, stretch, safe, unsafe, center, start, end, flex-start, flex-end.

将上面各个值分别代入justify-content及align-content,观察实际效果。

在父容器中统一设定子元素应否拉伸

默认情况下,grid container下所有子元素都会自动拉伸以填满grid布局空间。例如:

div.grid-container { display: grid; grid-template-columns: 150px 1fr; min-height: 150px; }
A
B
C
D
E
F

共有2列,第1列占用了150px的grid布局空间,第2列占用该行中grid布局空间的剩余空间。并且,由于grid布局空间最小高度为150px,在高度上的空间也很充裕,每个子元素的高度也被拉伸了。

但,查看每个子元素,其内容均只有一个字母,它们本身的内在尺寸(intrinsic size)实在无法铺满它们所实际占用的grid布局空间。

上面的代码只是确定了每个grid cell的大小。而在空间较大的grid cell内,如果再对子元素进行进一步的布局?

justify-items及align-items应运而生。它们均应用于grid container中,用以统一设置所有子元素的属性。前者用于水平方向,后者用于垂直方向。

div.grid-container { display: grid; grid-template-columns: 150px 1fr; min-height: 150px; justify-items: right; align-items: center; }
A
B
C
D
E
F

与上一个例子相比较,各个子元素仍呆在对应的grid cell之内,但每个子元素的边框仅围住了它们的内在尺寸的空间,并且依据我们的代码,在水平方向上靠右对齐,在垂直方向上居中对齐。

justify-items的值包括:normal, stretch, first, last, baseline, safe, unsafe, center, start, end, self-start, self-end, flex-start, flex-end, left, right, center, legacy(默认值)。

align-items的值包括:normal(默认值), stretch, first, last, baseline, safe, unsafe, center, start, end, self-start, self-end, flex-start, flex-end.

将上面各个值分别代入justify-items及align-items,观察实际效果。

在子元素中单独设定该元素应否拉伸

上一节,我们在父容器中统一设定了各个子元素应否拉伸,相对应地,justify-self及align-self可应用在各个子元素上,以设定该子元素不同于父容器统一设定的情况。

div.grid-container { display: grid; grid-template-columns: 150px 1fr; min-height: 150px; justify-items: right; align-items: center; } #a { justify-self: stretch; align-self: stretch; }
A
B
C
D
E
F

在父容器中,我们通过justify-items及align-items统一设定了所有的子元素应水平靠右垂直居中对齐,而在A元素中,通过justify-self及align-self将该元素改为水平及垂直均拉伸。

结语

CSS Grid的编排策略非常灵活,能应对各种各样的需求。然而,健壮的功能往往意味着庞大的身躯,因此,它实际远比从表面看上去时要复杂得多(本文的文本内容加上源代码,超过了2100行,全文历时将近半个月才完成)。

本教程通过丰富的实例,涵盖了CSS Grid最常用的90%以上的内容,希望对掌握并熟练驾驭CSS Grid能起到较大的帮助作用。

参考资源

  1. CSS Grid Layout Module Level 1
  2. CSS Grid Layout Module Level 2
  3. CSS Grid Layout Module Level 3
  4. CSS Box Sizing Module Level 3
  5. CSS Box Alignment Module Level 3
  6. Design From the Inside Out With CSS min-content
  7. MDN fit-content