11.11 CSS 显式网格布局

使用网格模板属性进行显式说明

网格模板区域(grid-template-areas)、网格模板行(grid-template-rows)和网格模板列(grid-template-columns),这3个属性共同显式定义了一个网格容器。而网格模板(grid-template)属性是一个用来同时设置这3个属性的速写(shorthand)。 grid items内容可能会超出显式网格,这时网格容器会自动生成隐式轨道(implicit track),这些隐式轨道的尺寸由 grid-auto-rows 和 grid-auto-columns 属性所确定。

显式网格的大小是由网格模板区域定义的行/列数以及在网格模板行/网格模板列属性定义了尺寸的行/列数中的较大者决定的。 任何由网格模板区域定义的行或列但没有在网格模板行/网格模板列中定义尺寸,则由grid-auto-rows或grid-auto-columns属性来确定大小。如果没有定义显式轨道,显式网格依然在每根轴线上包含一个网格线。

网格定位(grid-placement)属性中的数字索引从显式网格的边缘开始计算。如果从起始侧开始,索引为以1开始的正数。反之从结束侧开始,则为以-1开始的负数。

在开始具体属性说明之前,我们先把语法定义中的一些符号先解释下,方便后面章节的阅读。

  • 双与号(&&)分开两个或两个以上的单元,每个单元都必须存在,顺序无关。
  • 双或号(||)分开两个或两个以上的选项,至少有一个选项出现,顺序无关。
  • 单或号(|) 分开两个或两个以上的替换项,有且仅有一个选项出现。
  • 方括号([ ])用来分组。

轨道尺寸:grid-template-rows 和 grid-template-columns

属性名称: grid-template-columns, grid-template-rows
可用取值: none | <track-list> | <auto-track-list> | subgrid <line-name-list>?
初始值: none
用于: grid containers
继承的:
百分比: 参照相应内容区域的尺寸
媒体: visual
计算值: 指定长度
是否支持动画:

这两个属性用来定义一组空格分开的轨道列表(track list),轨道列表本身使用网格线名称和轨道尺寸函数来定义。 轨道尺寸函数可以是具体的长度、相对于网格容器的百分比、按实际填充内容计算(如min-content)或者可用空间片段。 它还可以通过minmax(min,max)函数来指定一个长度范围,也就是介于min和max之间的一个尺寸,其中min/max参数同样可以使用前面提到的这些尺寸定义方式。

grid-template-columns属性指定网格列的轨道列表,而grid-template-rows属性指定网格行的轨道列表。

轨道尺寸的正式语法格式定义

none | <track-list> | <auto-track-list> | subgrid <line-name-list>?
<track-list>       = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
<line-names>       = '[' <custom-ident>* ']'
<track-size>       = <track-breadth> | minmax( <track-breadth> , <track-breadth> )
<track-breadth>    = <length> | <percentage> | <flex> | min-content | max-content | auto

这里:

none
表示没有显式网格;任何行/列都将被隐式生成,并且它们的大小将由网格自动行和网格自动列属性决定
subgrid
表示网格将和其父网格轴线对齐。它们将沿用父网格的尺寸定义。
<length>
非负数的长度数值
<percentage>
参照网格容器行内尺寸的非负数百分比值,如果网格容器行内尺寸未定义,则被视作为 auto
<flex>
非负数的数值,使用单位 fr 来指定轨道的弹性因子。每个弹性尺寸的轨道会依据其弹性因子按比例占据剩余空间的一部分。

当出现在 minmax() 记号之外时,它隐含一个自动的min值,也就是 minmax(auto, <flex>)

max-content
表示使用grid track中grid items的最大内容贡献(max-content contribution)。
min-content
表示使用grid track中grid items的最小内容贡献(min-content contribution)。
minmax(min, max)
表示一个函数记号,用来指定一个长度范围,也就是介于min和max之间的一个尺寸。如果max小于min,则max被忽略,函数被视作min<flex> 值作为max时将设定轨道的弹性因子。作为min时,被视作0(或最小内容,如果网格容器有一个最小内容的尺寸约束)。
auto
作为max时,等同于max-content。作为min时,等同于min-content size。

CSS sizing level3规范中,和布局尺寸相关内容,经常会出现min-content size和max-content size以及contribution等术语:

  • min-content inline size 最小内容行内尺寸表示在尽可能使用软性(即非硬性指定)包含技术(比如自动换行)后,刚好包含了元素内容而不出现溢出的尺寸。
  • max-content inline size 和min-content inline size不同,最大内容行内尺寸表示在不使用任何软性包含技术时,在单行内(除非使用硬性换行)刚好包含了元素内容而不出现溢出的尺寸。
  • max-content block sizemin-content block size 对于块元素而言,这两者是等同的。
  • max-content contributionmin-content contribution 最大内容贡献和最小内容贡献分别对应于max-content, min-content size, 只不过在此之上,还需要计算入内外边距(margin/padding)以及边界(border)的尺寸。

这里有一个在线实例,可以帮助直观的理解min-content和max-content的区别。

使用方法示例1:
.grid{ grid-template-columns: 100px 1fr max-content minmax(min-content, 1fr);}

上面的语句创建了5条列网格线,构成了4个列:

  1. 第1条位于网格容器的起始边(start edge,即左边缘)。
  2. 第2条在第一条右边100px处。
  3. 第3条和前面网格线距离为弹性空间的1/2(因为有2列都是弹性尺寸,所以依据弹性因子按比例均分)
  4. 第4条和前面网格线距离为该列中grid items的最大宽度。
  5. 第5条和前面网格线距离为该列中不小于所有grid items的min-content size中的最大值,同时不大于剩下的1/2可用弹性空间。

当列定义中包含至少一个<flex>值时,如果非弹性尺寸(100px, max-content和min-content)的宽度加起来超过了网格容器的宽度,1fr尺寸会被解析为0,这样最后的网格线和网格容器起始边的距离为上述总和。 如果加起来小于网格容器的宽度,最后一条网格线就位于网格容器的终止边(右边缘)。

命名网格线(Named Grid Lines):[<custom-ident>*] 语法

虽然网格线可以通过数字索引来引用,给网格线命名还是要更容易理解和维护。 网格线可以在网格模板行(grid-template-rows)和网格模板列(grid-template-columns)属性中被显式命名,或通过网格模板区域(grid-template-areas)属性创建命名网格区域来隐式命名。

Image: Named Grid Lines.
命名网格线示例图

重复行和列: repeat() 函数记号

repeat函数用来简化行列定义,避免语句重复的行或列定义,但浏览器支持有限或表现不一致,这里仅保留章节以便日后更新,但不做更多说明。

弹性长度: fr 单位

以fr为尺寸单位的弹性长度代表网格容器中的自由空间(free space)的一部分(fraction,fr就是该单词的缩写)。自由空间的分配发生在所有非弹性轨道大小的尺寸函数均已达到其最大限度。 从可用空间中减去非弹性空间尺寸总和,就得到自由空间,然后再依据弹性尺寸行和列的弹性系数来按比例分配。

自由空间中列或行占比(share)的计算很直观,等于该列或行的<弹性因子> * <自由空间> / <所有弹性因子之和>。为此,用于minmax()函数最小位置的弹性长度被当作为0(一个非弹性长度)。

当可用空间是无限的(网格容器的宽高未被指定时),弹性尺寸的网格轨道按其内容来确定大小,同时保留其各自的比例。 每个弹性尺寸的网格轨道使用尺寸(used size)的计算通过确定每个弹性尺寸的网格轨道的最大内容尺寸(max-content size),并按照各自的弹性因子来划分这个尺寸以确定一个假想的‘1fr’尺寸。 其中最大的那个被当作最终决定的1fr长度,然后乘以每个网格轨道的弹性因子来确定其最终尺寸。

Subgrids: subgrid 关键字

subgrid用来定义嵌套grid布局的行为,这里暂不讲解,留待后续补充。

命名区域(Named Areas): grid-template-areas属性

Name: grid-template-areas
可用取值: none | <string>+
初始值: none
用于: grid containers
继承的:
百分比: n/a
媒体: visual
计算值: 指定值
是否支持动画:

此属性指定命名网格区域,该区域与任何特定的网格项无关,但可在网格定位(grid-placement)属性中引用。 网格模板区域(grid-template-areas)属性也提供了一个可视化的网格结构,使网格容器的整体布局更容易被理解。

属性取值含义如下:

none
网格容器没有定义网格模板区域。
<string>+
网格模板区域属性中每个单独的字符串(string)定义了一个行,而字符串中的每个单元(cell)定义了一个列。字符串和单元通过下面的方式来进行解析:

使用最长匹配语义来标记该字符串为如下标记(token)列表:

  • 一串名称编码点(name code points),代表一个命名单元标记(named cell token),其名称由code points(即Unicode编码空间中的字符)组成。
  • 一串单个或多个".",代表一个空单元标记(null cell token)
  • 一串空格(whitespace),代表不会生成任何标记。
  • 一串其它字符,代表一个垃圾标记(trash token)。

注意这些规则可能会生成和类型定义语法不完全匹配的单元名称,比如"1st 2nd 3rd",在其他属性中通过名字引用时需要转义一下:grid-row: \31st;用来引用名为1st的网格区域。

  • null cell token代表grid container中的未命名区域。
  • named cell token创建同名的named grid area。行间或行内多个同名named cell tokens将只创建一个named grid area,跨越相关单元。(就像电子表格应用中的合并单元格一样)。 create a single named grid area that spans the corresponding grid cells.
  • 一个trash token表示声明语句非法,存在一个语法错误。

所有字符串必须具有相同的列数,否则该声明无效。如果一个命名网格区域跨越多个网格单元,但这些单元格没有形成单一填充矩形,该声明是无效的。

注意:相邻且同名的named cell tokens会形成单一填充矩形。但W3规范在后续版本中也许会允许非矩形或非连接的网格区域。

我们现在再回过头来看前面提到的游戏应用布局,其中就使用了grid-template-areas属性:

隐式命名线(Implicit Named Lines)

网格模板区域属性从模板中的命名网格区域中创建隐式命名网格线。对于每个网格区域foo,4条隐式命名网格线为:2条名为foo-start,代表网格区域的起始行和起始列;而另外2条名为foo-end,代表网格区域的终止行和终止列。

这些线的行为和任何其他命名网格线一样,除了它们没有出现在网格模板行/网格模板列的值当中。即使定义了一个同名的显式线,隐式命名线依然存在。

隐式命名区域(Implicit Named Areas)

既然一个命名网格区域可以通过其所生成的隐式命名线来引用,通过显式添加相同形式的命名线(foo-star/foo-end)将事实上创建了一个隐式命名网格区域。 这样的隐式区域不会出现在网格模板区域的值当中,但仍可以被网格布局属性所引用。

显式网格速写:grid-template属性

Name: grid-template
可用取值: none | subgrid | <‘grid-template-columns’> / <‘grid-template-rows’> | [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
初始值: 参照各独立属性
用于: grid containers
继承的: 参照各独立属性
百分比: 参照各独立属性
媒体: visual
计算值: 参照各独立属性
是否支持动画:

网格模板属性是网格模板列、网格模板行和网格模板区域这3个属性的速写(简写)。它有几种不同的语法形式:

none
将所有三个属性设置为其初始值(无)。
subgrid
设置网格模板行和网格模板行为subgrid,网格模板区域为初始值。
<‘grid-template-columns’> / <‘grid-template-rows’>
将网格模板列和网格模板行设置为指定的值,并将网格模板区域设置为无。
grid-template: auto 1fr auto / auto 1fr;

等同于:

grid-template-columns: auto 1fr auto;
        grid-template-rows: auto 1fr;
        grid-template-areas: none;
        
[ <track-list> / ]? [ <line-names>? <string><track-size><line-names>? ]+
  • 将网格模板列设置为斜线(/)之前指定的轨道列表(或none,如果未指定)。
  • 将网格模板区域设置为斜线之后的字符串。
  • 将网格模板行设置成track-size后面的每个字符串(自动填充缺失的尺寸),并拼接每个尺寸前后定义的命名线。

此语法允许作者将轨道名称和大小与它们各自的网格区域进行对齐。

grid-template: auto 1fr auto /
  [header-top] "a   a   a"     [header-bottom]
    [main-top] "b   b   b" 1fr [main-bottom];

等同于

grid-template-columns: auto 1fr auto;
grid-template-rows: [header-top] auto [header-bottom main-top] 1fr [main-bottom];
grid-template-areas: "a a a"
                     "b b b";

尽管网格模板速写使用和单个属性相同的语法,但也会把隐式网格属性重置为其初始值。除非作者希望如此,建议使用单个属性定义。