CSS网格布局
本文最后更新于 2025年8月2日 下午
Demo:
https://github.com/xuekeven/learn_web/blob/main/dome/CSS/两栏_网格布局.html 。
https://github.com/xuekeven/learn_web/blob/main/dome/CSS/网格布局_照片墙.html 。
搭配:
https://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html 。
介绍使用
CSS 网格布局可以定义由行和列组成的二维布局,然后将元素放到网格中。有些元素可能只占据网格的一个单元,而另一些元素则可能占据多行或多列。网格的大小既可以精确定义,也可以根据自身内容自动计算。你既可以将元素精确地放置到网格某个位置,也可以让其在网格内自动定位,填充划分好的区域。
跟弹性布局类似,网格布局也是作用于两级的 DOM 结构。设置display: grid
的元素是一个网格容器(gridcontainer),它的子元素则变成网格元素(grid items)。使用display: grid
定义一个网格容器时,容器表现得像一个块级元素,100%填充可用宽度。也可以使用display: inline-grid
,这样元素就会在行内流动,且宽度只能够包含子元素,不过display: inline-grid
的使用频率不高。
有一点值得注意的是,使用网格布局并不会让弹性布局失去用武之地。使用弹性布局时,需要按照一定方式嵌套元素,用网格实现同样的布局需要修改 HTML 结构。
默认情况下,每个网格容器都会扩展并填满整个网格区域,但网格容器子元素不会填满整个网格容器,因此网格区域会出现多余的高度。一个简单的解决办法是用弹性布局,其子元素会填充高度。
与弹性布局配合
这两种布局方式有以下两个重要区别:弹性布局本质上是一维的,而网格布局是二维的;弹性布局是以内容为切入点由内向外工作的,而网格布局是以布局为切入点从外向内工作的。
弹性布局是一维的,所以它很适合用在相似的元素组成的行(列)上。它支持用 flex-wrap 换行,但是没法让上一行(列)元素跟下一行(列)元素对齐。相反网格布局是二维的,旨在解决一个轨道的元素跟另一个轨道的元素对齐的问题。
弹性布局让你在一行或一列中安排一系列元素,但是它们的大小不需要明确指定,每个元素占据的大小根据自身的内容决定。在网格布局中首先要描述布局,然后将元素放在布局结构中去。虽然每个网格元素的内容都能影响其网格轨道的大小,但这同时也会影响整个轨道大小,进而影响这个轨道里其他网格元素的大小。
当设计要求元素在两个维度上都对齐时使用网格布局;当只关心一维元素排列时使用弹性布局。在实践中,这通常(并非总是)意味着网格更适合用于整体的网页布局,弹性布局更适合对网格区域内特定元素布局。继续使用两者,你就会对不同情况下该用哪种布局得心应手。
网格剖析
- 网格线(grid line)
网格线构成了网格的框架。一条网格线可以水平或者垂直,也可以位于一行或一列的任意一侧。
如果指定了 grid-gap 的话,它就位于网格线上。
- 网格轨道(grid track)
一个网格轨道是两条相邻网格线之间空间。网格有水平轨道(行)和垂直轨道(列)。
- 网格单元(grid cell)
网格上的单个空间,水平和垂直的网格轨道交叉重叠的部分。
- 网格区域(grid area)
网格上的矩形区域,由一个到多个网格单元组成。该区域位于两条垂直网格线和两条水平网格线之间。
编号网格线
网格轨道定义好后,要将每个网格子元素放到特定的位置上。浏览器给网格里的每个网格线都赋予了编号。网格线编号从左上角为 1 开始递增,负数则指向从右下角开始的位置。
可以在 grid-column 和 grid-row 属性中用网格线的编号指定网格子元素位置。如果想要一个网格元素在垂直方向上跨越 1 号到 3 号网格线,就需要给元素设置grid-column: 1 / 3
,同理设置grid-row: 3 / 5
让元素在水平方向上跨越 3 号到 5 号网格线。这两属性一起就能指定一个元素应该放置的网格区域。
需要注意,grid-column 是 grid-column-start 和 grid-column-end 的简写;grid-row 是 grid-row-start 和 grid-row-end 的简写。中间的斜线只在简写属性里用于区分这两个值,斜线前后的空格不作要求。
可以用特别的关键字 span 可以用来指定 grid-row 和 grid-column 的值。这个关键字告诉浏览器子元素需要占据一个网格轨道。如果没有指出具体是哪一行或列,浏览器将会根据网格子元素的布局算法(placement algorithm)自动将其放到合适的位置。布局算法将子元素放在网格上可以容纳该元素的第一处可用空间。
命名网格线
有时候记录所有网格线的编号实在太麻烦,尤其是在处理很多网格轨道时。为了简单,可以给网格线命名,并在布局网格子元素时使用网格线名称而不是编号。
使用 grid-template-columns 和 grid-template-rows 声明网格轨道时,可以在中括号内写上网格线的名称,且可以同时命名两个名称。两个名称之间用空格隔开,之后可以任选一个名称使用。
还有一个彩蛋:将两个网格线命名为 left-start 和 left-end,就定义了一个叫作 left 的区域,这区域覆盖两个网格线之间的区域。-start 和 -end 后缀作为关键字,定义两者之间区域。子元素设置grid-column: left
,它就会跨越从 left-start 到 left-end 的区域。
命名时重复使用同一个名称合法,可以在名称后跟数字区分是第几个网格线。可以以各种方式命名网格线。它们在网格里的用法也是五花八门,这取决于每个网格特定的结构。
命名网格区域
不用计算或者命名网格线,直接用命名的网格区域将网格子元素定位到网格中。想要实现这一方法需要借助网格容器的 grid-template 属性和网格子元素的 grid-area 属性。
grid-template-areas 属性使用一种 ASCII art 的语法,可以直接在 CSS 中画一个可视化的网格形象。该声明给出了一系列加引号字符串,每一个字符串代表网格的一行,字符串内用空格区分每一列。
每个命名的网格区域必须组成一个矩形。不能创造更复杂的形状,比如 L 型或者 U 型。也还可以用句点(.)作为名称,这样便能空出一个网格单元。
注意,区域的命名会影响到网格线。每个区域的起始网格线会被自动命名为:区域名-start;终止网格线会被自动命名为:区域名-end。与命名网格线的彩蛋呼应。
隐式网格
在某些场景下,你可能不清楚该把网格子元素放在网格的哪个位置上。处理大量子元素时,挨个指定元素的位置未免太不方便。当元素是从数据库获取时,元素的个数很可能未知。在这些情况下,以一种宽松的方式定义网格可能更合理,剩下的交给布局算法放置网格子元素。
这时需要用隐式网格(implicit grid)。使用 grid-template-columns,grid-template-rows 属性定义网格轨道创建的是显式网格(explicit grid),但有些网格子元素仍可以放在显式轨道外,此时便会自动创建隐式轨道以扩展网格,从而包含这些元素。
隐式网格轨道默认大小为 auto,也就是说它们会扩展到能容纳网格子元素内容。设置 grid-auto-columns 和 grid-auto-rows 则是为隐式网格轨道指定每列列宽和每行行高。在指定网格线的时候,隐式网格轨道并不会改变负数的含义。负的网格线编号仍是从显式网格的右下开始的。
容器属性
- grid-template-columns,grid-template-rows(网格列数行数)
- grid-template-areas,grid-template(网格区域)
- grid-auto-columns,grid-auto-rows(隐式网格列数行数)
- grid-auto-flow,grid(子元素放置顺序)
- grid-column-gap,grid-row-gap,grid-gap(网格列间隔行间隔)
- justify-content,align-content,place-content(内容区域整体水平垂直位置)
- justify-items,align-items,place-items(子元素内部水平垂直位置)
grid-template-columns,grid-template-rows
grid-template-columns 属性定义网格的列数和每列列宽,同时可以给网格线命名。
grid-template-rows 属性定义网格的行数和每行行高,同时可以给网格线命名。
新单位 fr 代表每一列(行)的分数单位(fraction unit)。这个单位跟弹性布局中 flex-grow 属性表现一样。不一定非得用分数单位,可以使用其他的单位,比如 px、em 或百分数,也可以混搭这几种单位。
有时候,重复写同样的值非常麻烦,尤其网格很多时。新方法 repeat() 函数在声明多个网格轨道的时候提供简写方式。使用 repeat() 函数可以定义不同的重复模式,比如grid-template-columns: repeat(3, 2fr 1fr)
会重复三遍该模式,从而定义六个网格轨道,重复的最终结果是2fr 1fr 2fr 1fr 2fr 1fr
。还可以将 repeat() 作为一个更长的模式的一部分。比如grid-template-columns: 1fr repeat(3, 3fr) 1fr
,最终结果是1fr 3fr 3fr 3fr 1fr
。可以看出来因为展开写法无法一目了然,所以才产生了 repeat() 这种简写方式。
有时候我们不想给一个网格轨道设置固定的尺寸,但是又希望限制它的最小值和最大值。这时候需要用到 minmax() 函数。它接收两个值:最小尺寸和最大尺寸。浏览器会确保网格轨道的大小介于这两者之间。
关键字 auto 表示由浏览器决定长度。轨道大小设置为 auto,轨道会根据自身内容扩展。
关键字 auto-fill 是特殊值,只要网格放得下,浏览器就会尽可能多地生成轨道,并且不会跟指定大小(minmax() 值)的限制产生冲突。于是两者在一起,会让网格在可用的空间内尽可能多地产生网格列或行,则轨道大小会取 minmax() 函数中的最小值。虽然产生尽可能多轨道,但是网格子元素不一定能填完轨道。
若网格子元素不够填满所有网格轨道,auto-fill 就会导致一些空的网格轨道。若不希望出现空的网格轨道,可以使用关键字 auto-fit 代替 auto-fill。它会让非空的网格轨道扩展,填满可用空间。具体选择 auto-fill 还是 auto-fit 取决于你是想要确保网格轨道的大小,还是希望整个网格容器都被填满。
grid-template-areas,grid-template
grid-template-areas 属性用于定义区域,一个区域由单个或多个单元格组成。grid-template 属性是
grid-template-columns、grid-template-rows、grid-template-areas 属性的合并简写形式。
grid-template-areas 属性使用一种 ASCII art 的语法,可以直接在 CSS 中画一个可视化的网格形象。该声明给出了一系列加引号字符串,每一个字符串代表网格的一行,字符串内用空格区分每一列。
每个命名的网格区域必须组成一个矩形。不能创造更复杂的形状,比如 L 型或者 U 型。也还可以用句点(.)作为名称,这样便能空出一个网格单元。
注意,区域的命名会影响到网格线。每个区域的起始网格线会被自动命名为:区域名-start;终止网格线会被自动命名为:区域名-end。与命名网格线的彩蛋呼应。
grid-auto-columns,grid-auto-rows
grid-auto-columns 属性用于当设置隐式网格时,浏览器自动创建的网格的每列列宽。
grid-auto-rows 属性用于当设置隐式网格时,浏览器自动创建的网格的每行行高。
它们与 grid-template-columns 和 grid-template-rows 写法完全相同。如果不指定这两个属性,浏览器完全根据单元格内容的大小,自动决定新增网格的每列列宽和每行行高。
grid-auto-flow,grid
当不指定网格上元素的位置时,元素会按照其布局算法自动放置。默认情况下,布局算法会按元素在标记中的顺序将其逐列逐行摆放。当一个元素无法在某一行容纳(也就是说该元素占据了太多网格轨道)时,算法会将它移动到下一行寻找足够大的空间容纳它。网格布局模块规范提供了一个属性 grid-auto-flow ,它可以控制布局算法的行为。
grid-auto-flow 属性控制布局算法放置网格子元素的顺序。grid 属性是
grid-template-rows、grid-template-columns、grid-template-areas、
grid-auto-rows、grid-auto-columns、grid-auto-flow 属性的合并简写形式。
- row:先行后列。元素优先放在网格行中,只有当一行填满了才会移动到下一列。默认值。
- column:先列后行。元素优先放在网格列中,只有当一列填满了才会移动到下一行。
- row dense:先行后列,并且尽量紧密填满不出现空格。
- column dense:先列后行,并且尽量紧密填满不出现空格。
关键字 dense 让布局算法紧凑地填满网格里的空白,尽管这会改变某些网格子元素的顺序。加上该关键字,子元素就会“回填”大元素造成的空白区域,但 HTML 源码顺序不变。因此紧凑的 grid-auto-flow 方式可能会导致元素出现的顺序跟 HTML 里不一致。
grid-column-gap,grid-row-gap,grid-gap
grid-column-gap 属性设置网格列与列的间隔。
grid-row-gap 属性设置网格行与行的间隔。
grid-gap 属性是 grid-column-gap 和 grid-row-gap 属性的合并简写形式。在使用该值时,省略第二个值,浏览器就会假定第二个值等于第一个值。
justify-content,align-content,place-content
justify-content 属性设置整个内容区域整体在容器里面的水平位置(左中右)和间隔。
align-content 属性设置整个内容区域整体在容器里面的垂直位置(上中下)和间隔。
(特别是当网格子元素的整个内容区域整体无法填满网格容器时)
place-content 属性是 align-content 和 justify-content 属性的合并简写形式。使用该值时,省略第二个值,浏览器就会假定第二个值等于第一个值。
当 place-content 属性与 grid-gap 属性冲突时,place-content 属性优先。
- start:对齐容器的起始边框(弹性布局里则是 flex-start)。默认值。
- end:对齐容器的结束边框(弹性布局里则是 flex-end)。
- center:容器内部居中。
- stretch:项目大小没有指定时,拉伸占据整个网格容器。
- space-between:项目间的间隔相等,项目与容器边框之间没有间隔。
- space-around:项目两侧的间隔相等。所以项目之间的间隔是项目与容器边框的间隔一倍。
- space-evenly:项目与项目的间隔相等,项目与容器边框的间隔也相等(弹性布局不支持)。
justify-items,align-items,place-items
justify-items 属性设置网格子元素的内容在网格子元素里面的水平位置(左中右)。
align-items 属性设置网格子元素的内容在网格子元素里面的垂直位置(上中下)。
place-items 属性是 align-items 和 justify-items 属性的合并简写形式。使用该值时,省略第二个值,浏览器就会假定第二个值等于第一个值。
- stretch:拉伸,占满单元格的整个宽度。默认值。
- start:对齐单元格的起始边缘。
- end:对齐单元格的结束边缘。
- center:单元格内部居中。
子元素属性
- grid-column-start,grid-column-end,grid-column(子元素左右网格线)
- grid-row-start,grid-row-end,grid-row(子元素上下网格线)
- grid-area(子元素区域)
- justify-self,align-self,place-self(子元素内部水平垂直位置)
grid-column-start,grid-column-end,grid-column
grid-column-start 属性设置网格子元素左边框所在的垂直网格线的编号或名字。
grid-column-end 属性设置网格子元素右边框所在的垂直网格线的编号或名字。
grid-column 属性用网格线编号或名字指定子元素垂直方向位置。grid-column 属性是 grid-column-start 和 grid-column-end 属性的合并简写形式,推荐使用简写属性,可使用 span 关键字直接占据网格轨道个数。
grid-row-start,grid-row-end,grid-row
grid-row-start 属性设置网格子元素上边框所在的水平网格线的编号或名字。
grid-row-end 属性设置网格子元素下边框所在的水平网格线的编号或名字。
grid-row 属性用网格线编号或名字指定网格子元素水平方向位置。grid-row 属性是 grid-row-start 和
grid-row-end 属性的合并简写形式,推荐使用简写属性,可使用 span 关键字直接占据网格轨道个数。
grid-area
grid-area 属性指定网格子元素项目放在哪一个区域(前提为使用 grid-template-areas 给区域命名)。
该属性是 grid-row-start、grid-column-start、grid-row-end、grid-column-end 属性的合并简写形式,可以直接指定网格子元素的位置。
justify-self,align-self,place-self
justify-self 属性设置网格子元素的内容在网格子元素里面的水平位置(左中右),跟 justify-items 属性用法完全一致,只作用于单个子元素。
align-self 属性设置网格子元素的内容在网格子元素里面的垂直位置(上中下),跟 align-items 属性用法
完全一致,只作用于单个子元素。
place-self 属性是 align-self 和 justify-self 属性的合并简写形式。在使用该值时,如果省略第二个值,浏览器就会假定第二个值等于第一个值。
- stretch:拉伸,占满单元格的整个宽度。默认值。
- start:对齐单元格的起始边缘。
- end:对齐单元格的结束边缘。
- center:单元格内部居中。