| 导语 CSS GRID布局是一个二维的布局系统,相对比一维的flexbox布局更加灵活方便。当然,这就让它看起来也相对于flexbox更加复杂一些,但我们只要把一些基本的概念弄清楚,弄明白CSS GRID里应用于父容器跟子元素上的各个属性的原理以及这些属性是如何表现的,这个看似复杂的css布局功能也并不是想象中那么复杂。下面,就让我们来一一剖析这些属性吧。
由于相关属性比较多,为了方便大家理解以及记忆相关知识点,我理了一些简单的属性关系图,也是本文大概的大纲目录:
图中container为网格容器,item为网格子元素,后面的属性包含关系逻辑,表示可用前一级进行下一级多个属性值简写;aligns节点表示对齐方式,justify- / align- 前缀可跟 -itmes / -content / -self后缀进行两两组合,箭头指向表示该属性应用于container还是item上,每个属性对应的属性值以及相关效果请查看下边每个属性的详细介绍
Figure 1 Exemplary Flex Layout Example
Figure 2 Exemplary Grid Layout Example
讲到CSS GRID布局,避免不了的就是拿flexbox来进行对比。(我在前年的时候写过一篇关于flexbox相关的文章)。两个css布局属性都会涉及到对父容器跟子元素相关的属性,大家可以通过这篇文章去跟我前年写的flexbox通过父容器以及子元素的各个属性进行一一对比。
这篇文章我会按照上面flexbox那篇文章类似的方式来进行,拆分父容器跟子元素下各个属性的介绍,以及一些个人心得。
一、基本术语:
在本文开始之前,我们先来了解一些CSS GRID相关的基本术语:
1.Grid容器(父容器):
这里作为父容器需要将其display设置为grid(或inline-grid)
2.网格子元素(Grid Item)
父容器的直属子元素才是网格元素(grid item),而次级子元素则不是。如上面代码中,只有类名为item的div节点才是网格元素。
3.网格线(Grid Line)
这个比较容易理解,就是用来分隔网格结果的界线。
4.网格轨道(Grid Track)
最简单的说法,就是行或者列。
5.网格单元(Grid Cell)
网格系统的最小单元格,由两条相邻的行线跟两条相邻的列线间的空隙位置。
6.网格区域(Grid Area)
四条网格线(两条行线跟两条列线),注意这里跟网格单元的区别,网格单元是最小单元格,而网格区域则不一定是最小单元格,有可能由多个网路单元格组成。
二、父容器的属性设置
1.display
要让我们的容器变成网格容器,建立一个网格系统,最主要的就是给改容器设置display为grid。可以有三个值来设置:
1.grid--生成一个块状元素的网格容器
2.inline-grid -- 生成一个行内元素的网格容器
3.subgrid -- 如果容器本身是网格内的一个子元素,可以通过设置subgrid来让该子元素容器继承其父容器网格的行/列值,而不需要再单独设置。
这里需要注意的是,从W3C的规范来看,subgrid语法还并不成熟,我测试的时候发现设置display:subgrid;并不起效,具体的大家可以看看W3C文档中关于subgrid的部分。
| 子网格还并未在任何浏览器中实现,并且随时有可能从规范中移除。
2.grid-template-columns / grid-template-rows
这里两个值的定义分别为定义网格容器下网格轨道的尺寸,分别为 列宽 / 行高。多个值之间以空格分隔,这里的值代表各个轨道的尺寸值,空格表示分隔线(也可以用[line-name]来给分隔线命名):
1.<track-size>--该值可以是一个带px单位的长度值,也可以是一个百分比(%)或者是网格系统专有的单位fr(表示可用空间的分数)还可以设置为auto(表示该尺寸根据内容自适应);各种不同单位的值可以混合使用
2.<line-name>--给相应的网格线命名,当没有给定改值的时候,网格线默认以数字命名(1开始);可以在中括号内用空格分隔的方式给同一条网格线多个命名
3.可以使用repeat()方法实现重复
4.可以结合minmax()方法来创建轨道尺寸的时候控制轨道的最小 / 最大尺寸
See the Pen grid-template-columns / grid-template-rows by john (@happyTIME-2) on CodePen.
大家可以在点击我在codepen的链接,在上面的代码中尽情地去尝试各种不同的写法(点击上面codepen右上角,进入编辑模式),看最后呈现的效果。
3.grid-template-areas
1.<grid-area-name>--这里跟子元素的grid-area通过name方式定义的区域挂钩
2. "." -- 点字符表示一个空的网格单元(这里我发现一个有意思的东西:其实不用点号,用其他字符也可以实现同样的效果,即使是中文字符也行)
3.none -- 不定义网格区域
这里需要注意几个点:1)name方式定义的grid-area默认为一个单元网格尺寸;2)这里每行设置的单元格数必须一致;3)当我们通过这种方式给我们网格区域时,会自动给相应的网格线命名,比如上图中main区域,其行线及列线的起始线就会被自动设置为main-start,行线及列线的结束线就命为main-end,其他元素就可以通过对应的网格线进行位置放置。(看我在codepen中container2中对item-b的定义)
4.grid-template
这个属性,就是上面三个属性值的简写方式:
1.none -- 对三个属性都不设置值,三个属性就为默认的初始值(auto auto none)
2.subgrid -- 将grid-template-rows / grid-template-columns的值设置为subgrid,grid-template-areas的值为none
3.<grid-template-rows> / <grid-template-columns>--给grid-template-rows / grid-template-coumns设置指定值,grid-template-areas为none
eg:
如上代码,跟下边的相等:
5.grid-gap(grid-column-gap / grid-row-gap
用来指定网格线的尺寸,简单可以理解为行 / 列之间的间隔。
也可以使用简写方式:
当只给定一个值的时候,默认两个值都为改值。
这里grid-gap只会给两条轨道之间的间隔,而不会设置边缘的值。有一点需要注意的是,某个轨道上没有子元素,且轨道尺寸为零(设置为auto或百分比,见前面我再grid-templat-columns / grid-template-rows红色部分的内容)的情况下,grid-row-gap的尺寸还是会增加的(可以理解为轨道尺寸为零,两条网格线并不会重叠)
See the Pen grid-gap by john (@happyTIME-2) on CodePen.
6.grid-auto-rows / grid-auto-columns
前面我们可以通过grid-template-rows / grid-template-columns来创建一个确定轨道尺寸的网格系统,也就是所谓的显式网格。而当我们将网格元素的位置放置到我们创建的显示网格系统外的时候,会自动地根据你给网格元素的位置放置情况创建隐式网络来容纳改网格元素。
给父容器如上设置的时候,会创建一个这样的网格系统:
我们继续给itemb跟itemc两个网格子元素放置到我们创建的网格系统外的时候,我们会发现我们的网格系统自动地变成了如下的情况:
这里我们将itemb跟itemc根据网格线定位到网格外面,但我们创建的网格并没有对应的列线4、5、6以及对应的行线3,但我们可以看到,我们的网格系统自动地创建了相应的行列来容纳我们的网格元素。由于这些参考线并不是显式存在的,我们可以理解为,这些参考线之间的轨道尺寸为0。
我们可以通过grid-auto-rows / grid-auto-columns来给我们的隐式网格轨道(就上面尺寸为0的轨道)设定为指定的值。
这时候我们再看我们的网格系统:
大家可以去我在codepen的示例去尽情地折腾,偷偷告诉大家一个彩蛋,我们上面说我们没有设置grid-auto-columns跟grid-auto-rows值的时候,网格外无网格子元素的轨道尺寸为0,你会发现我再codepen代码中给wrap容器加了一个宽度值,你可以把width去掉,同时干掉grid-auto-columns的设置,再看看上面说的轨道尺寸为0的轨道会有什么变化?当你继续增大itemb中grid-column的值,你会发现什么样的变化?
See the Pen grid-auto-cloumns / grid-auto-rows by john (@happyTIME-2) on CodePen.
7.grid-auto-flow
前面我们可以发现,当我们创建了网格系统后,网格容器内的网格子元素如果没有指定其放置情况,子元素会自动按序排列置于网格系统中。而这个属性可以设置自动填充的网格元素按什么样的方式进行自动填充:
1.row -- 按行从左到右的顺序自动填充网络的方式
2.column -- 按列从上到下的顺序自动填充网络的方式
3.dense -- 当前面有空白区域的时候,允许小尺寸的网格元素提前放置填充空白区域的顺序(该属性会导致网格子元素不一定按顺序排列)
See the Pen grid-auto-flow by john (@happyTIME-2) on CodePen.
大家可以发现,前面我们用grid-template把grid-template-rows / grid-template-columns / grid-template-areas三个集合到了一起简写,而官方文档却没有把grid-auto-rows / grid-auto-columns / grid-auto-flow集合为grid-auto进行简写。???
8.grid
从前面的规律我们可以知道,这个属性就是定义在父容器上的grid各个属性的简写方式。包含:grid-template-rows / grid-template-columns / grid-template-areas /grid-auto-rows/ grid-auto-columns / grid-auto-flow 。这里并没有对grid-column-gap / grid-row-gap进行定义,其实通过这个方式创建网格系统的时候,会给这两个值设置为默认值0。
1.none -- 设置所有的子属性值为各属性默认的初始值
2.<grid-template-rows> / <grid-template-columns> -- 设置grid-template-rows / grid-template-columns的值,其他的子属性值为默认的初始值
3.<grid-auto-flow> [<grid-auto-rows> [/ <grid-auto-columns> ] ] -- 给定了三个值的情况下,则三个属性分别对应设定的值,若只省略了grid-auto-columns的值,则该值设定为跟grid-auto-rows的值一致,若grid-auto-rows跟grid-auto-columns均没给定对应值,则这两个值均为默认的初始值
下边两个写法的效果一致,eg:
以下两种写法的效果也一致:
还能用更复杂的写法来实现:
上面的代码等同于:
三、网格子元素(Grid Items)属性
1.grid-column-start / grid-column-end / grid-row-start / grid-row-end
通过指定的网格线来确定该网格子元素(Grid Item)在网格系统中的位置。grid-column-start / grid-row-start表示该网格元素的开始线,grid-column-end / grid-row-end 表示该网格元素的结束线。
1.<line> -- 网格线可以是一个已通过网格标号的网格线数字,也可以是一个通过网格系统已命名的网格线名
2.span <number> -- 表示该网格元素将跨越的网格轨道的数量
3.span <name> -- 结合给定的起始网格线的位置表示将该网格元素延伸到指定命名的网格线轨道
4.auto -- 表示让该网格元素自动放置,默认跨度为一个轨道。
See the Pen grid-column / grid-rows by john (@happyTIME-2) on CodePen.
这里需要注意的是,跟前面父容器属性中提到的一样,当将某个网格元素(Grid Item)通过网格线方式来确定位置的时候,如果其位置超出父容器通过显示定义的网格的时候,会自动根据你对该网格元素网格线位置的定义扩展到隐式网格(相当于网格系统会自动往外延伸,不通过grid-auto-rows / grid-auto-columns对隐式网格轨道尺寸定义的时候,默认无元素的轨道尺寸为0);2)网格起始线为-1的时候,为显示网格的最后一条网格线,结束网格线为负数的情况下,表示从右向左计数;
2.grid-column / grid-row
从字面可以看出,这两个属性是对上面属性的简写方式,grid-column:grid-column-start+grid-column-end / grid-row:grid-row-start+grid-row-end
1.<start-line> / <end-line> -- 两个值都有指定的时候,表示该网格子元素位置的开始及结束对应的网格线;没有给定结束网格线的值时表示从指定的开始网格线跨度一个轨道尺寸结束。
3.grid-area
1.<name> -- 给该网格元素命名,让其能跟用grid-template-areas定义的网格系统相关联
2.<row-start> / <column-start> / <row-end> / <column-end> -- 是前面通过网格线定义区域的简写方式,这里需要注意一下各个参数对应的线的顺序
四、对齐方式
这里的对齐方式会涉及到父容器(Grid Container)跟网格子元素(Grid Items)。
这里为了方便大家记忆,不至于混淆使用,我提供一个简单的方法,justify- 前缀表示按row方向的对齐方式,align- 前缀;表示按column方向的对齐方式。-self后缀(应用于网格子元素本身)表示单个网格子元素(Grid Item)内的内容在网格子元素内的对齐方式,-items后缀(应用于网格父容器上)表示网格容器内所有网格子元素(Grid Items)的内容在其所在网格子元素内的对齐方式,-content后缀表示(当网格容器内的网格元素总的尺寸小于网格容器尺寸)网格容器内的网格子元素(Grid Items)本身在容器内的对齐方式。
这样我们把上面的前缀跟后缀组合一下,理解起来就简单很多了:
1.justify-content / align-content(应用于container)
当网格容器内的所有网格子元素的总尺寸小于网格容器的尺寸的时候,通过该属性设置网格内所有子元素在网格容器的row轴方向的对齐方式(justify-content) / 通过该属性设置网格内所有子元素在网格容器的column轴方向的对齐方式(align-content)
1.start -- 将网格对齐网格容器的左端(justify-) / 顶部(align-)
2.end -- 将网格对齐网格容器的右端 (justify-) / 底部(align-)
3.center -- 将网格在网格容器内居中对齐
4.stretch -- 将自动拉伸网格元素的尺寸让网格能够填满整个网格容器(我在codepen调试发现该属性并没有效果)
5.space-around -- 将多余的空间根据子网格元素的数量进行平均分配,每个item左右各占一份尺寸(所以两个网格子元素之间会占两份的剩余空间,如果设置了grid-template-gap的情况下,会先将gap的尺寸留出来后再平均分配,这时候两个网格子元素之间的空间占gap的尺寸加两份剩余空间)
6.space-enenly -- 跟space-around类似,但此时两个网格子元素之间只占一份剩余空间,而不会像space-around那样占据两份剩余空间(grid-template-gap的值跟上面一样,会先预留)
7.space-between -- 网格容器两端不占据空间,剩余的空间平均分配(这时候有没有设置grid-template-gap并不会有什么影响)
See the Pen justify-content / align-content by john (@happyTIME-2) on CodePen.
有跨多个轨道尺寸的网格子元素,在使用space-around / space-evenly / space-between对齐属性后会对改网格子元素的尺寸有变化,因为该网格子元素占据了其中的网格子元素之间的剩余空间。
justify-content:space-around
justify-content:space-evenly
2.justify-items / align-items (应用于container)
设置所有网格子元素内的内容在row轴方向上的对齐方式(justify-items) / 设置所有网格子元素内的内容在column轴方向上的对齐方式(align-items)
1.start -- 设置所有网格子元素内的内容在网格子元素内左对齐(justify-)/ 顶对齐(align-)
2.end -- 设置所有网格子元素内的内容在网格子元素内右对齐(justify-)/ 底对齐(align-)
3.center -- 设置所有网格子元素内的内容在网格子元素内([垂直])居中对齐
4.stretch -- 设置所有网格子元素内的内容在网格子元素拉伸填满整个网格子元素
See the Pen justify-items / align-items by john (@happyTIME-2) on CodePen.
通过这种方式设置的对齐方式,也可以通过justify-self / align-self对某个网格子元素进行对齐方式覆盖
justify-items:center
align-items:center
3.justify-self / align-self(应用于item)
跟上面justify-items / align-items的功能类似,让应用了该属性的网格子元素内的内容在row轴方向对齐(justify-self) / 在column轴上对齐(align-self)
1.start -- 设置该网格子元素内的内容在网格子元素内左对齐(justify-)/ 顶对齐(align-)
2.end -- 设置该网格子元素内的内容在网格子元素内右对齐(justify-)/ 底对齐(align-)
3.center -- 设置该网格子元素内的内容在网格子元素内居中对齐
4.stretch -- 设置所有网格子元素内的内容在网格子元素拉伸填满整个网格子元素
See the Pen justify-self / align-self by john (@happyTIME-2) on CodePen.
五、简单应用场景
网格系统由于灵活多变的布局属性,以及弹性轨道的性质,结合媒体查询使得其在响应式布局上非常简单方便,下边我们来简单创建一个响应式的布局:
正常情况下创建一个大尺寸下如下布局:
结合媒体查询,在小屏幕下:
See the Pen respponsive by john (@happyTIME-2) on CodePen.
简直是方便的不能再方便了。除此以外,像之前flexbox类似的那样,实现一个尺寸不固定的内容在页面内的垂直居中也是分分钟搞定的事情。
六、浏览器支持情况
从caniuse查询的情况我们知道,在高级浏览器下对css grid的支持度已经比较好了(ie下基本不支持),所以在移动端页面中,基本上可以放心大胆地使用了。现在css grid的规范还在完善中,一些支持度不好的浏览器相信就可以慢慢用上了。
七、资料参考
- https://css-tricks.com/snippets/css/complete-guide-grid/
- https://www.w3.org/TR/css-grid-1/
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout
- https://www.smashingmagazine.com/2018/04/best-practices-grid-layout/?utm_source=CSS-Weekly&utm_campaign=Issue-311&utm_medium=web
- https://gridbyexample.com/learn/
- https://abookapart.com/products/get-ready-for-css-grid-layout