图解CSS: Grid布局(Part2)
在上一节中,主要介绍了 CSS 网格布局中的一些重要概念和相关术语。从这些概念和术语中,从侧面也再次说明CSS 网格布局到目前为止是最为复杂的一个布局系统,换句话说,CSS 网格布局系统中会有很多属性,这些属性和 Flexbox 布局有些类似,有些属性只能作用于网格容器,有些属性只能作用于网格项目。而在这篇文章中,先从可用于网格容器的属性,即使用display
属性创建网格容器和网格项目, grid-template-areas
、grid-template-columns
和 grid-template-rows
以及这三个属性的简写属性grid-template
来定义显式网格。如果感兴趣的请继续往下阅读。
网格布局中的属性
CSS 网格布局中涉及到的属性和 Flexbox 布局类似,可分为两个部分,其中一部分可用于 网格容器 的属性;另一部分是可用于 网格项目 的属性。接下来我们来看看这些属性的使用和所起的作用。
可用于网格容器的属性
到目前为止,CSS 网格布局模块 Level 1 、 Level 2 和 Level 3 几个不同版本规范的定义的可以用于网格容器的属性主要有:
在这些属性列表中,有些属性到现在还未得到浏览器的支持,或者仅得到个别主流浏览器的支持,比如前面提到的 subgrid
属性,而且有些还仅是实验性的属性,比如 Level 3 中的 masonry-auto-flow
。这里要特别提出的是 CSS 网格布局模块 Level 3 ,目前还是处于规范的 ED 阶段,里面所提到的部分属性还处于不确定状态,有可能会随着后面的规范完善程度随之改变,因此,我们在这里不会在 Level 3 规范中提到属性花多少时间。
创建网格容器和网格项目 display
创建网格容器和网格项目很简单,只需要在容器上显式设置 display
的值为 grid
或 inline-grid
,该容器就成为网格容器,其子元素以及文本节点,伪元素就成为网格项目。
grid
:display
取值为grid
时会使一个元素产生一个网格容器,当放在流式布局中时,它是块状的,称之为块网格容器inline-grid
:display
取值为inline-grid
时会使一个元素产生一个网格容器,当放在流式布局中时,它是内联的,称之为内联的网格容器
一个网格容器为其内容建立了一个独立的网格格式化上下文(Grid Formatting Content,即 GFC)。这与建立一个独立的块格式化上下文(Block Formatting Content,即 BFC)是一样的,只是使用了网格布局而不是块布局。网格容器的内容被布置成一个网格,网格线形成了每个网格项目所含块的边界。
网格容器不是块状容器,因此一些块状布局而设计的属性在网格布局中并不适用。特别是:
float
和clear
对网格项目没有影响。然而,float
属性仍然会影响到网格容器的子项上的display
的计算值,因为这发生在网格项被确定之前vertical-align
对一个网格项没有影响::first-line
和::first-letter
伪元素不适用于网格容器,而且网格容器不会为其祖先提供第个格式化的行或第一个字母
如果一个元素指定 display
的值是 inline-grid
,并且该元素是浮动的或绝对定位的,那么该元素的 display
的计算值是 grid
。
虽然在一个元素上显式设置 display
的值为 grid
或 inline-grid
值,但你看到的效果和 display
值为 block
或 inline
相似的效果,比如下面这个示例:
<!-- HTML -->
<div class="grid__container">
<div class="grid__item">Grid Item</div>
Anonymous Item
<div class="grid__item" style="float: left">Float Element</div>
</div>
<span>Inline Element</span>
/* CSS */
.grid__container {
--display: grid;
display: var(--display);
}
这是因为没有在网格容器上显式创建任何行或列,但事实上此时创建的是一个真实的网格容器,只不过这个时候网格是一个单列网格,网格的行由网格容器的子元素所决定,并且它们在单列中一个接一个地显示。从视觉上看,它们就像块状元素。
你可能已经看到了,示例中的“Anonymous Item” 是一个文本节点,并没有包裹在一个元素中,但它属于网格容器的子元素,它也成为一个网格项。网格容器的伪元素,比如示例中的 ::before
和 ::after
也将成为网格项目。
示例中 grid
切换到 inline-grid
时,网格容器变成一个内联级的盒子(内联网格容器),然而,其直接的子元素仍然是网格项目,其表现行为与块容器中的网格项目相同。
另外,在示例中的容网格容器 div.grid__container
下面有一个 span
元素放置了一串文字,当 display
的值为 grid
时,该网格容器是个块级网格容器,其表现形为类似于块级元素,所以 span
元素会在另一行开始排列;当 grid
切换到 inline-grid
时,该网格容器就变成了一个内联级网格容器,所以 span
元素可以在它的旁边显式。
注意,你可能在某些介绍 CSS 网格布局的相关教程中有看到
display
取值subgrid
。这里要告诉大家的是,在写这篇文章的时候,subgrid
值已从display
属性值列表中删除。
在 CSS 中创建网格容器是使用 display
的值来创建的,有关于 display
更深入的介绍,可以阅读《Web布局:display
属性》一文。
网格容器的尺寸
网格容器的尺寸是使用它所参与的格式化上下文的规则来确定的:
- 作为一个块格式化上下文中的块级框,它的尺寸与建立格式化上下文的块级框一样,与非替换的块级框一样计算自动内联尺寸
- 作为一个内联格式化上下文中的内联级框,它的尺寸与原子内联级框(内联块)一样
在内联和块格式化上下文中,网格容器的自动块尺寸是其最大内容(max-content
)的大小。
一个网格容器的最大内容尺寸(max-content
)或最小内容尺寸(min-content
)是该网格容器在适当的轴上的轨道尺寸(包括网格沟槽)的总和。
简单地来说,网格容器的尺寸可以像其它元素容器一样,使用尺寸相关的属性(比如 width
、max-width
、min-width
、height
、max-height
、min-height
与及其对应的逻辑属性)来设置。比如下面这个示例:
.grid__container {
--grid: grid;
--width: 40;
--height: 30;
display: var(--grid);
grid-template-columns: repeat(3, 200px);
gap: 10px;
width: calc(var(--width) * 1vw);
height: calc(var(--height) * 1vh);
overflow: auto;
}
就该示例而言,我们在网格容器上显式设置了 width
和 height
(拖动滑块可以动态调整它们的值),同时使用 grid-template-columns
指定了每列列宽是 200px
,每行行高根据网格项目自身高度尺寸来决定。这样一来,在拖动滑块时,网格容器的 width
值有可能小于三列加沟槽的总和(此例是 320px
),也有可能大于它们的总和:
- 当
width
小于320px
时,网格容器会出现水平滚动条(因为容器显式设置了overflow: auto
) - 当
width
大于320px
时,网格容器会有空白空间留出
网格容器的高度和宽度类似的,只不过没有显式使用 grid-template-rows
来显式指每行的行高,而是由网格项目盒模型自身决定。你将看到的效果如下:
除此之外,我们还可以通过 grid-template-columns
和 grid-template-rows
以及 gap
等属性来控制网格容器的尺寸:
.grid__container {
--col-1: 100;
--col-2: 100;
--col-3: 100;
--row-1: 50;
--row-2: 50;
--row-3: 50;
display: grid;
grid-template-columns:
calc(var(--col-1) * 1px)
calc(var(--col-2) * 1px)
calc(var(--col-3) * 1px);
grid-template-rows:
calc(var(--row-1) * 1px)
calc(var(--row-2) * 1px)
calc(var(--row-3) * 1px);
gap: 10px;
}
拖动示例中的滑块,可以看到网格容器尺寸的变化:
网格项目的尺寸
在网格布局中,除了 grid-template-columns
、grid-template-rows
和 grid-template-areas
以及 grid-column
、grid-row
和 grid-area
等属性可以决定网格项目尺寸之外,还有我们熟悉的设置元素尺寸的相关属性,比如《图解CSS: 元素尺寸的设置》一文中提到的 width
、height
等属性。
除此之外,网格项目的尺寸还会受设置在网格项目上的对齐属性,比如 align-self
、justify-self
的影响。有关于这方面的更详细的介绍,我们放到后面介绍网格中的对齐方式一节中来介绍。
定义网格
元素显式设置 display
的值为 grid
或 inline-grid
只是帮助我们创建了一个网格容器(格式化上下文而以)。但定义一个网格还是需要依赖于其他的一些属性,比如前面示例中多次出现的 grid-template-columns
、grid-template-rows
、grid-template-areas
,还有 grid-auto-rows
和 grid-auto-columns
等属性。根据不同的属性,我们定义的网格又分为 显式网格 和 隐式网格 。其中 grid-template-columns
、grid-template-rows
、grid-template-areas
三个属性定义的网格被称为 显式网格 ;grid-auto-rows
和 grid-auto-columns
定义的网格被称为 隐式网格 。
定义显式网格:grid-template-columns/rows/areas
在 CSS 网格布局,如果在网格容器中显式使用了 grid-template-columns
、grid-template-rows
和 grid-template-areas
三个属性指定了网格轨道,那么这个网格就被称为 显式网格 。
grid-template-rows
和 grid-template-columns
先来看 grid-template-rows
和 grid-template-columns
。
我们可以在 grid-template-rows
和 grid-template-colums
属性上设置用空格分隔开来的多个数值,这些数值列表定义了网格的行和列。这些值同时代表网格轨道的大小,它们之间的空格代表网格线。这两个属性可接受的值:
grid-template-columns: none | <track-list> | <auto-track-list>
grid-template-rows: none | <track-list> | <auto-track-list>
其中 none
是其初始值,表示此属性不创建显式网格轨道(尽管显式网格轨道仍可由 grid-template-areas
创建)。
注意,在没有显式网格的情况下,任何行和列都将被式生成,它们的大小将由
grid-auto-rows
和grid-auto-columns
属性决定。
<track-list> | <auto-track-list>
指将网格轨道列表指定为一系列的轨道尺寸函数和网络线名称。每个轨道尺寸函数都可以被指定为一个长度(<length>
)、网格容器大小的百分比(<percentage>
)、占据列或行的内容的测量值(内容的宽高),或者网格中自由空间的一部分(即 fr
指定的轨道尺寸)。也可以使用 minmax()
函数指定一个范围值,它可以结合之前提到的任何机制,为列或行指定单独的最小和最大轨道尺寸。
轨道列表(Track List)的语法规则如下:
<track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
<auto-track-list> = [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>? <auto-repeat>
[ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
<explicit-track-list> = [ <line-names>? <track-size> ]+ <line-names>?
<track-size> = <track-breadth> | minmax( <inflexible-breadth> , <track-breadth> ) | fit-content( <length-percentage> )
<fixed-size> = <fixed-breadth> | minmax( <fixed-breadth> , <track-breadth> ) | minmax( <inflexible-breadth> , <fixed-breadth> )
<track-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
<inflexible-breadth> = <length-percentage> | min-content | max-content | auto
<fixed-breadth> = <length-percentage>
<line-names> = '[' <custom-ident>* ']'
看到上面的语法规则是不是有点晕。莫急,我们通过一些简单的示例帮助大家理解它们。
在网格容器上显式设置 grid-template-columns
:
.grid__container {
grid-template-columns: 180px 20% auto 1fr 10vw;
}
示例使用 grid-template-columns: 180px 20% auto 1fr 10vw
将网格容器分成了五列,其值分别是每列的列宽:
grid-template-columns
的值有各种不同单位的值,比如 px
、%
、vw
,有关键词 auto
,还有网格布局中独有的单位( <flex>
单位)fr
。后面我们将会花一些篇幅来介绍它们在网格中的使用。回到这个示例中来,从效果上你可能发现了,虽然只在网格容器上使用了 grid-template-columns
定义了一个五列的网格,但事实效果是一个五列两行的网格,这是因为网格容器中有十个网格项目,但只定义了五列,那么第六个网格项目就会自动流到新的一行,即第二行,并且以网格项目的高度来定义计算每行的高,相当于 grid-template-rows
设置了值 auto
。
我们在上面的示例的基础上显式添加 grid-template-rows
:
.grid__container {
grid-template-columns: 180px 20% auto 1fr 10vw;
grid-template-rows: 25% auto;
}
这样一来,grid-template-columns
和 grid-template-rows
就一起定义了一个五列两行的网格,和前面示例不同的是,指定了行的高度:
这两个属性的值除了 auto
关键词之外,还可以接受以下 CSS 函数 和关键词:
fit-content(<length-percentage>)
minmax(min, max)
max-content
和min-content
比如下面这个示例,我们在 grid-template-columns
中使用这几个函数,创建了一个四列网格。第一列是该轨道的最小内容尺寸(min-content
),第二列是该轨道的最大内容尺寸(max-content
),第三列如果内容大于10rem
(即fit-content(10rem)
)的话该列宽度为 max-content
,第四列是 minmax(10rem, 1fr)
,一个 10rem ~ 1fr
之间的范围值,大于等于 min
(10rem
)值,并且小于等于 max
(1fr
)值,如果 max
值小于 min
值,则该值会被视为 min
值:
.grid__container {
grid-template-columns: min-content max-content fit-content(10rem) minmax(10rem,1fr);
}
示例中的每列会根据网格项目的最小内容,最大内容以及网格内容等决定实际列宽:
我们还可以在这两个属性中,使用 <flex>
单位 fr
和 repeat()
来创建等宽的列和等高的行。比如我们创建一个等分的三行四列的网格,可以像下面这样创建:
.grid__container {
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 1fr);
}
上面的代码等同于:
.grid__container {
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
}
该示例把网格容器的宽度均分成四等份,高度均分为三等份,每列的宽相等,每行的高相等:
repeat()
函数还有一个特性,可以把多列(或多行)重复性用该函数来描述,比如说第一行高度是 12rem
,第二行高度是 1fr
,第三行高度是 12rem
,第四行高度是 1fr
,我们就可以在 grid-template-rows
上像下面这样使用repeat()
函数:
grid-template-rows: repeat(2, 12rem 1fr);
// 等同于
grid-template-rows: 12rem 1fr 12rem 1fr;
另外,还可以将 repeat()
和 minmax()
、 auto-fill
和 auto-fit
关键词结合在一起使用,比如:
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
比如下面这个示例,我们将这几个功能都结合在一起:
.grid__container {
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-rows: repeat(2, 12rem 1fr);
}
在 minmax()
函数中使用 auto-fit
和 auto-fill
最终得到的效果是不相同的,这里暂不详细介绍,同样放到后面来阐述。
当你创建一个网格容器时,网格轨道之间会自动创建网格线,且会自动分配正负数。使用 grid-template-columns
和 grid-template-rows
时可以显式的给网格线命名,不过要注意它的使用语法, 网络线名称放在中括号内 ,我们可以像下面这样使用:
.grid__container {
grid-template-columns: [col1-start] 180px [col2-start] auto [col3-start] 30% [col3-end];
grid-template-rows: [row1-start] 180px [row2-start] auto [row3-start] 1fr [row3-end];
}
使用浏览器开发者工具,我们可以选择网格线的命名(字符串)或网格线索引号显示,两者对应关系如下图所示:
如果在 grid-template-columns
和 grid-template-rows
中显式命名网格线名称的话,可以给同一条网格线命名一个以上的名称,比如下面示例中的第 2
条列网格线,同时命名成 [col1-end col2-start]
,这样命名也好理解,这条网格线既是第一列的结束网格线也同时是第二列的起始网格线:
.grid__container {
grid-template-columns: [col1-start] 180px [col1-end col2-start] 20% [col2-end col3-start] 1fr [col3-end col4-start] 10vw [col4-end];
grid-template-rows: [row1-start] 200px [row1-end row2-start] 1fr [row2-end];
}
如果在同一条网格线上命名两个名称的话,在开发者工具中显示网格线的时候,也会把两个名称显示出来:
要是定义的网格每列(或每行)尺寸都相等时,会使用 repeat()
函数让定义网格变得更简易。这种方式在前面有向大家演示过,这里要说的是,我们同样可以将网格线的命名用于 repeat()
函数中,比如:
.grid__container {
grid-template-columns: repeat(4, 1fr [col]);
}
// 等同于
.grid__container {
grid-template-columns: 1fr [col] 1fr [col] 1fr [col] 1fr [col];
}
用开发者工具显示命名的网格线名称时,你会发现从第二条至最后一条,每条网格线名称都是 col
。另外这个示例并未grid-template-rows
中显式命名网格线名称,因此在行网格线只能看到数字索引号(网格线):
值得注意的是,使用 grid-template-columns
和 grid-template-rows
定义网格时,不能缺少网格轨道尺寸(<track-size>
)的设置,否则会被视为语法错误,相当于未显式使用这两个属性定义网格。
.grid__container
grid-template-columns: [col1-start] [col1-end col2-start] [col2-end col3-start] [col3-end];
grid-template-rows: [row1-start] [row1-end row2-start] [row2-end row3-start] [row3-end];
}
我们花了很长篇幅介绍了 grid-template-columns
和 grid-template-rows
的使用,这两个属性涉及到的值方式很多。这里简单小结一下。前面列出 <track-list> | <auto-track-list>
时有很多种不同类型的选项,并且都在示例中向大家呈现了,这里简单的阐述一下它们的含义:
<line-names>
: 网格线名称,指的是显式命名的网格线名称,即'[' <custom-ident>* ']'
<track-size>
: 轨道尺寸(行高或列宽),可以是<length-percentage>
值,<flex>
(即带fr
单位的值),也可以是一些关键词(比如min-content
,max-content
,auto
),也可以是minmax()
和fit-content()
函数<track-repeat>
: 即repeat()
函数,该函数可以传递两个参数,第一个参数是重复的数量(<integer [1,∞]>
),第二个参数是轨道尺寸(<track-size>
),也可以同时是轨道尺寸和网格线名称(<track-size> + <line-names>
)<fixed-size>
: 可以是<length-percentage>
,minmax()
<fixed-repeat>
:有点类似于<track-repeat>
,不同的是第二个参数是<fixed-size> + <line-names>
<inflexible-breadth>
:它的值类型主要有<length-percentage>
,min-content
,max-content
和auto
从这些值类型也能发现,grid-template-columns
和 grid-template-rows
属性取值的类型是多么的灵活,这也从侧面也告诉大家,grid-template-columns
和 grid-template-rows
因为过于灵活,也造成理解成本更大,也过于复杂。不过,我们可以将其简化一下,掌握下面这些值的使用规则就可以掌握好这两个属性的使用:
<length>
: 非负值的长度大小,比如100px
,10rem
,30vw
等长度值<percentage>
: 非负值的百分比值,它相对于网格容器宽度计算。采用百分比时有一个细节需要注意,如果网格容器的尺寸大小依赖网格轨道大小时,百分比值将被视为auto
<flex>
: 非负值,用单位fr
来定义网格轨道大小的弹性系数,有点类似于 Flexbox 布局中的flex-grow
属性,按比例分配网格容器的剩余空间。如果fr
用于minmax()
函数中时,它将是一个自动最小值,即可minmax(auto, <flex>)
max-content
: 是一个用来表示以网格项的最大的内容来占据网格轨道min-content
: 是一个用来表示以网格项的最大的最小内容来占据网格轨道minmax(min, max)
: 是一个来定义大小范围的函数,大于等于min
值,并且小于等于max
值。如果max
值小于min
值,则该值会被视为min
值。最大值可以设置为网格轨道系数值<flex>
,但最小值则不行auto
: 如果轨道为最大时,等同于<max-content>
,为最小时,则等同于<min-content>
fit-content([<length> | <percentage>])
: 相当于min(max-content, max(auto, argument))
,类似于auto
的计算(即minmax(auto, max-content)
),除了网格轨道大小值是确定下来的,否则该值都大于auto
的最小值repeat([<positive-integer> | auto-fill | auto-fit], <track-list>)
: 表示网格轨道的重复部分,以一种更简洁的方式去表示大量而且重复列的表达式
简而之,这些都是用来确定网格轨道大小的方法。后面将会花一节内容专门和大家探讨,这主要是因为定义网格轨道(行或列)有很多选项和单位可以选择,只有掌握了什么时候应该使用什么,才能更好的定义好网格。
特别声明 ,
grid-template-columns
和grid-template-rows
两属性分别在 Level 2 和 Level 3 两个规范围中新增了subgrid
和masonry
两个属性,可用来创建子网格和瀑布流布局。这两个属性在 CSS 网格布局中是新增的特性,非常的强大,也很实用,能帮助我们实现很多复杂的布局效果。因此,后面会把这两个特性单独拿出来和大家探讨。感兴趣的同学,可以关注一下,或者直接跳到这两个章节。
grid-template-areas
创建显式网格,除了 grid-template-columns
和 grid-template-rows
之外还可以使用 grid-template-areas
属性,该属性也是运用于网格容器上,但相对于 grid-template-columns
和 grid-template-rows
属性要简单地多。grid-template-areas
语法规则很简单:
grid-template-areas: none | <string>+
grid-template-areas
属性指定了命名的网格区域,它们不与任何特定的网格项目相关联,但是它们可以和一些网格定位属性关联起来,比如 grid-row-start
、grid-row-end
、grid-column-start
和grid-column-end
,也可双和一些简写属性关联起来,比如 grid-row
、grid-column
和 grid-area
。grid-template-areas
属性的语法还提供了网格结构的可视化,使网格容器的整体布局更容易理解。该属性的主要接受的值有:
none
:表示没有命名的网格区域,同样也没有显式的网格轨道被这个属性定义(尽管显式的网格轨道仍然可以由grid-template-colums
或grid-template-rows
创建)<string>
:为grid-template-areas
属性列出的每一个单独的字符串创建一行,一个字符串中用空格分隔的每一个单元会创建一列。多个同名的,跨越相邻行或列的单元格称为网格区域。非矩形的网格区域是无效的。
简单地说,grid-template-areas
中的每个字符串值都代表网格中的一个行。每个字符串中以空格分隔的一组值代表网格中的列。这些字符串中的每个网格项目的名称都被映射到特定的 HTML 元素。我们先来看一个简单的示例,使用 grid-template-areas
创建一个常见的三列的页面布局效果:
这个布局有 header
、brand
、aside
、sidebar
、main
和 footer
几个区域。其对应的 HTML 结构如下:
<!-- HTML -->
<div class="container">
<header>Header Section</header>
<div class="brand">Brand Section</div>
<aside>Aside Section</aside>
<main>Main Section</main>
<div class="sidebar">Sidebar Section</div>
<footer>Footer Section</footer>
</div>
在.container
元素中使用grid-template-areas
像下面这样定义一个网格:
.container {
display: grid;
grid-template-areas:
"header header header header header header header header header header header header"
"brand brand brand brand brand brand brand brand brand brand brand brand"
"aside aside aside main main main main main main sidebar sidebar sidebar"
"footer footer footer footer footer footer footer footer footer footer footer footer";
}
上面的代码定义了一个12
列4
行的网格:
上图中实心蓝框框区的区域就是grid-template-areas
定义的网格区域,但离我们所需要的布局效果距有较大的差异,并未达到所需的布局效果。我们需要在对应的 HTML 元素上使用grid-area
引用grid-template-areas
已命名的网格区域的名称:
header {
grid-area: header;
}
.brand {
grid-area: brand;
}
aside {
grid-area: aside;
}
main {
grid-area: main;
}
.sidebar {
grid-area: sidebar;
}
footer {
grid-area: footer;
}
虽然使用grid-area
属性将对应的HTML元素放置到相应的网格区域中,但你也可能发现了,网格的列宽和行高是自动计算的,为了让布局更合理一些,指定相应的列宽和行高,可以在上面的代码中添加grid-template-columns
和grid-template-rows
:
.container {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 80px 160px 1fr 60px;
grid-template-areas:
"header header header header header header header header header header header header"
"brand brand brand brand brand brand brand brand brand brand brand brand"
"aside aside aside main main main main main main sidebar sidebar sidebar"
"footer footer footer footer footer footer footer footer footer footer footer footer";
}
最终效果如下:
示例网格线、网格区域,网格轨轨道相关的信息如下图所示:
使用grid-template-areas
定义网格区域是有一些规则的。如果违反这些规则将使数值无效,因此你的布局将无法实现。第一条规则是:
规则① :必须描述一个完整的网格,即网格上的每一个单元格都必须被填充。
比如:
.grid__container {
grid-template-areas:
"header header header header"
"nav main main aside"
"footer footer footer footer";
}
每个命名对应的就是一个网格单元格:
第二条规则:
规则② :一连串的空白,代表什么都没有,将造成
grid-template-areas
语法错误!
即,使用一连串空格来代表一个命名区域。比如说,在上面的示例基础上,将最后一个header
有一连串的空格符来替代,即缺少一个:
.grid__container {
grid-template-areas:
"header header header "
"nav main main aside"
"footer footer footer footer";
}
这将造成grid-template-areas
语法错误:
第三条规则:
规则③ :在网格命名中可以使用一个或多个
.
(U+002E
),代表一个空单元格。
我们使用“ 规则① ”已经实现了用区域填充网格,不留空余空间,而且也不能使用“ 规则② ”来给网格留出空的网格单元,因为“规则②”会造成grid-template-areas
的语法错误。如果想给网格留出空白的单元格,可以使用一个点(.
)或多个点(...
)来表示。比如说,我们希望把footer
区域和main
左右对齐,即footer
左边留出nav
区域,同时右边留出aside
区域,就可以像下面这样使用.
来表示:
.grid__container {
grid-template-areas:
"header header header header"
"nav main main aside"
". footer footer .";
}
效果如下:
使用开发者工具查看的网格如下图所示:
很多时候为了让布局更整齐,还可以使用多个“.
”符号,如果在多个点符号之间没有任何空格,比如 ...
,那么它们就会被计算为一个单元格,即.
等同...
:
grid-template-areas:
"header header header header"
"nav main main aside"
". footer footer .";
// 等同
grid-template-areas:
"header header header header"
"nav main main aside"
"... footer footer ...";
用多个“.
”符号表示一个网格单元格,它的好处是对于复杂的布局来说很容易让行列对齐,但需要特别注意,在使用多个点时,点与点之间绝对不能有任何空格符号,一旦有空格,那么将表示独立的网格单元格,比如:
.grid__container {
grid-template-areas:
"header header header header"
"nav main main aside"
". . footer ...";
}
网格审查结果如下:
第四条规则:
规则④ : 一个命名听单元格命名(令牌)创建一个具有相同名称的命名网格区域。在行内和行间的多个命名的单元格命名(令牌)创建单一的命名的网格区域,横跨相应的网格单元。简单的说,跨行命名相同的网格区域名称,可以达到合并单元格的作用。
比如像下面这个示例,我们需要将侧边栏(sidebar
)和页脚(footer
)合并起来(main
和 footer
区域具有相同的宽度)。
可以像下面这样来命名网格区域:
/* Before */
.container {
grid-template-areas:
"header header"
"sidebar main"
"footer footer"
}
/* After */
.container {
grid-template-areas:
"header header"
"sidebar main"
"sidebar footer"
}
你可以尝试着切换上面的单选按钮,查看布局的变化:
如果用浏览器的布局审查器,你可以查看到两者的差异:
使用网格区域命名实现单元格的合并时需要注意一点,“一个命名的网格区域跨越了多个网格单元格,但这些单元格并没有形成一个单一的填充矩形,则声明无效”,比如说一个类似L
的形状:
第五条规则:
规则⑤ :任何其他字符的序列,会代表一个垃圾标记(Trash Token),会使声明无效。
使用 grid-template-areas
属性中使用 <string>
命名网格区域时,如果使用一些其他的字符,比如像数字 1
、#
、1st
等,将会被视为无效的声明。
但在 grid-template-areas
中可以使用 Emoji 字符:
也可以是纯 HTML 实体符(HTML Symbols),还可以是 Emoji 和 HTML Symbols 混合在一起使用:
虽然这样来命名网格区域能定义网格,但不推荐这么使用。因为命一个有语义的名称要比这些字符更易于理解。
最后一条规则:
规则⑥ :当序列化
grid-template-areas
的<string>
值的指定值或计算值时,相邻两字符串(网格区域命名)被一个空格(U+0020
)隔开,当两者之间有多个空格符时,会被视为一个,其他空格将会被忽略。
比如下面两个grid-template-areas
中的字符命最终结果是一样的:
也就是说,在使用grid-template-areas
定义网格时,在命名应该遵循这些规则,不然容易造成语法上的错误,使该属性失效:
- 规则① :必须描述一个完整的网格,即网格上的每一个单元格都必须被填充
- 规则② :一连串的空白,代表什么都没有,将造成
grid-template-areas
语法错误 - 规则③ :在网格命名中可以使用一个或多个
.
(U+002E
),代表一个空单元格 - 规则④ : 一个命名听单元格命名(令牌)创建一个具有相同名称的命名网格区域。在行内和行间的多个命名的单元格命名(令牌)创建单一的命名的网格区域,横跨相应的网格单元。简单的说,跨行命名相同的网格区域名称,可以达到合并单元格的作用
- 规则⑤ :任何其他字符的序列,会代表一个垃圾标记(Trash Token),会使声明无效
CSS 网格布局中使用 grid-template-areas
指定网格区域,创建了显式的网格,同时也将生成隐式的网格线名。比如说一个网格区域的名称叫 foo
,它会对应的生成四个隐式的网络线名,分别分配在该网格区域的四边,即:
- 网格区域行开始和列开始,被称为
foo-start
- 网格区域行结束和列结束,被称为
foo-end
这些随着网格区域名称创建的隐式网格线名和其他网格线名一样,只是它们不是使用grid-template-rows
和grid-template-colummns
中是式创建的网格线名。而且这些隐式的网格线名同样可以用于grid-row
和grid-column
中,用于放置网格项目。比如下面这个示例:
.container {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 80px 1fr 60px;
grid-gap: 1rem;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
header {
/* ①: grid-area: header; */
/* ②: grid-area: header-start / header-start / header-end / header-end; */
grid-row: header-start / header-end;
grid-column: header-start / header-end;
}
aside {
/* ①: grid-area: sidebar; */
/* ②: grid-area: sidebar-start / sidebar-start / sidebar-end / sidebar-end; */
grid-row: sidebar-start / sidebar-end;
grid-column: sidebar-start / sidebar-end;
}
main {
/* ①: grid-area: main; */
/* ②: grid-area: main-start / main-start / main-end / main-end; */
grid-rows: main-start / main-end;
grid-column: main-start / main-end;
}
footer {
/* ①: grid-area: footer; */
/* ②: grid-area: footer-start / footer-start / footer-end / footer-end; */
grid-rows: footer-start / footer-end;
grid-column: footer-start / footer-end;
}
grid-area
引用网格区域名和引用隐式网格线以及grid-column
和grid-row
引用隐式网格线来放置网格项目,最终的效果都是一致的:
即 grid-template-areas
声明的网格区域名称,对应创建的隐式网格线如下所示:
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
/* 相当于 grid-template-columns 和 grid-template-rows 像下面这样创建显式网格线 */
grid-template-columns:
[header-start sidebar-start footer-start] 200px [sidebar-end main-start] 1fr [header-end main-end footer-end];
grid-template-rows:
[header-start] 80px [header-end sidebar-start] 1fr [sidebar-end footer-start] 60px [footer-end];
而且我们显式在grid-template-columns
和 grid-template-rows
显式设置了网格线名称,对于grid-template-areas
隐式创建的网格线名称也不会有任何的影响,只会在隐式的网格线名称上新增显式声明的网格线名称:
.container {
display: grid;
grid-template-columns:
[hd-col-start sb-col-start ft-col-start] 200px [sb-col-end mn-col-start] 1fr [hd-col-end mn-col-end ft-col-end];
grid-template-rows:
[hd-row-start] 80px [hd-row-end sb-row-start] 1fr [sb-row-end ft-row-start] 60px [ft-row-end];
grid-gap: 1rem;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
反过来,如果在grid-template-columns
和grid-templae-rows
中按grid-template-areas
隐匿创建网格线方式来声明网格线名称,即显式添加相同形式的命名网格线名称(foo-start/foo-end
),则会有效的创建一个命名为foo
的网格区域,只不过这种方式创建的网格区域是一个隐式的网格区域,也就是没有在grid-template-areas
中显式声明网格区域。隐式创建的网格区域同样可以在网格项目上使用grid-area
引用对应的网格区域来放置。比如下面这个示例:
.container {
display: grid;
grid-gap: 1rem;
grid-template-columns:
[header-start sidebar-start] 200px [sidebar-end main-start footer-start] 1fr [header-end main-end footer-end];
grid-template-rows:
[header-start] 80px [header-end sidebar-start main-start] 1fr [main-end footer-start] 60px [sidebar-end footer-end];
}
header {
grid-area: header;
}
aside {
grid-area: sidebar;
}
main {
grid-area: main;
}
footer {
grid-area: footer;
}
特别声明:示例中用到的
grid-row
、grid-column
和grid-area
是用于网格项目中的属性,其所起作用和如何实用这里暂且不详细阐述,后面在介绍网格项目相关的属性时,会详细介绍。这里你只需要知道他们是用于网格项目,可以根据网格线名称和网格区域名称将网格项目放置到指定位置即可!
grid-template
grid-template
是 grid-template-rows
、grid-template-columns
和 grid-template-areas
三个属性的简写属性,该属性语法规则如下:
grid-template:
none |
[ <'grid-template-rows'> / <'grid-template-columns'> ] |
[ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?
可设值:
none
:即恢复默认设置。行列隐式生成,grid-auto-rows
与grid-auto-columns
决定网格轨道尺寸<grid-template-rows> / <grid-template-columns>
: 指定grid-template-rows
与grid-template-columns
之值,并设grid-template-areas
的值为none
[<line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?
:将grid-template-areas
设置为所列的字符串(<string>
);将<grid-template-columns>
设置为 斜线/
的<explicit-track-list>
轨道列表(如果没有指定,则为0
);将grid-template-rows
设置为每个字符串后面的<track-size>
(默认为auto
),并在每个尺之前(或之后)接上定义的命名的网格线名称
我们来看两个简单地示例,来增强对 grid-template
属性的理解。先来看 grid-template
取值为 <grid-template-rows> / <grid-template-columns>
:
.container {
grid-template: 80px 1fr 60px / 1fr min(75ch, 80vw) 1fr;
}
/* 等同于 */
.container {
grid-template-rows: 80px 1fr 60px;
grid-template-columns: 1fr min(75ch, 80vw) 1fr;
grid-template-areas: none;
}
使用网格审查器查看布局结果:
再来看更复杂一点的,就是grid-template
包括了grid-template-rows
、grid-template-columns
和 grid-template-areas
。基于上面的示例,我们可以像下面这样使用grid-template
:
.container {
grid-template:
[header-start] "header header header" 80px [header-end]
[nav-start main-start sidebar-start] "nav main sidebar" 1fr [nav-end main-end sidebar-end]
[footer-start] "footer footer footer" 60px [footer-end]
/
1fr min(75ch, 80vw) 1fr;
}
/* 等同于 */
.container {
grid-template-areas:
"header header header"
"nav main sidebar"
"footer footer footer";
grid-template-rows:
[header-start] 80px [header-end nav-start main-start sidebar-start] 1fr [nav-end main-end sidebar-end footer-start] 60px [footer-end];
grid-template-columns: 1fr min(75ch, 80vw) 1fr;
}
在grid-template
简写属性中,grid-template-columns
也可以对应加上网格线的命名:
.container {
grid-template:
[header-start] "header header header" 80px [header-end]
[nav-start main-start sidebar-start] "nav main sidebar" 1fr [nav-end main-end sidebar-end]
[footer-start] "footer footer footer" 60px [footer-end]
/ [header-start nav-start footer-start] 1fr [nav-end main-start] min(
75ch,
80vw
)
[main-end sidebar-start] 1fr [header-end sidebar-end footer-end];
}
/* 等同于 */
.container {
grid-template-areas:
"header header header"
"nav main sidebar"
"footer footer footer";
grid-template-rows:
[header-start] 80px [header-end nav-start main-start sidebar-start] 1fr [nav-end main-end sidebar-end footer-start] 60px [footer-end];
grid-template-columns:
[header-start nav-start footer-start] 1fr [nav-end main-start] min(75ch,80vw) [main-end sidebar-start] 1fr [header-end sidebar-end footer-end];
}
效果都是一样的:
两种写法的网格线命名差异如下:
注意 : 在这些轨道列表中不允许使用
repeat()
函数,因为轨道的目的是在视觉上与“ASCII艺术(ASCII Art)”中的行、列一对一对排列。
另外有一点需要注意,使用简写的 grid-template
属性时不会重置隐含的网格属性(grid-auto-columns
、grid-auto-rows
和 grid-auto-flow
),不过后面我们要介绍的另一个简写属性 grid
会包含这几个属性。
在实际使用中,不管是对网格布局熟悉的同学,还是初学者,就我个人而言,不建议使用grid-template
(或grid
)属性,因为他过于复杂,除了不易于理解,还很容易出错。换句话说,如果你不是非常熟悉他的话,请使用 grid-template-areas
、grid-template-rows
和 grid-template-columns
三个属性。
如果你阅读到这里的话,我想你知道怎么使用 grid-template-areas
、grid-template-rows
和 grid-template-columns
(以及它们的简写属性 grid-template
)来定义 显式网格,但这仅是第一步。因为这里面还涉及很多其他的知识,比如网格轨道尺寸的设置、网格线的命名等。那么接下来,我们花一些篇幅来介绍使用grid-template-rows
和 grid-template-columns
定义网格轨道(行或列)怎么来做选择,因为使用它们创建网格轨道有很多选项和单位可以选择,只有掌握了什么时候应该使用什么,才能更好的定义好网格。
待续 ...
这篇文章主要介绍了如何使用 display
来创建网格容器,以及 使用 grid-template-areas
、grid-template-columns
和 grid-template-rows
以及它们的简写属性grid-template
定义显式网格。这仅仅是可用于网格容器的常见的几个属性。在介绍grid-template-areas
、grid-template-columns
、grid-template-rows
和 grid-template
定义显式网格时,提到grid-template-rows
和grid-template-columns
定义网格轨道可选项和单位比较丰富,只有掌握了什么时候应该使用什么,才能更好的定义好网格。那么在下一节中将围绕着这个主题展开。如果你感兴趣的话,请继续关注后续的相关更新。