📃 原文地址:An Interactive Guide to CSS Grid
👨💼 原文作者: Joshw Comeau
📂 归类:CSS
🗓️ 初次发布:2023 年 11 月 21 日
🔄 最近更新:2025 年 5 月 9 日
我们接上篇 - CSS Grid 交互式指南(译)(上) 完成剩下的内容
👶 分配子元素(Assigning children)
默认情况下,CSS Grid 的算法会将每个子元素分配到第一个未被占用的网格单元格中,这就像工匠在浴室地板上铺瓷砖一样,一块接一块地铺上去。
不过这里有个很酷的事情:我们可以将子元素分配到任意的单元格!
子元素甚至可以跨越多个行或列。
👇 下面这个交互式示例展示了这个特性。你可以点击/按住并拖动,以将某个子元素放置在网格中的不同位置:
💡 译注:此处示例为可视化拖动演示,展示了 grid 中
grid-column
,grid-row
等属性的作用。强推大家点进原文自己互动看看~

grid-row
和 grid-column
这两个属性可以让我们指定子元素应当占据哪些轨道(track) 。
如果我们希望子元素只占据一行或一列 ,可以直接通过数字指定。
例如:
css
grid-column: 3;
这表示该子元素会放在第 3 列 中。CSS Grid 也允许子元素跨越多行或多列 。这时候的语法会使用 /
斜杠来标识起始和结束的位置,例如:
css
.child {
grid-column: 1 / 4;
}
乍一看这像是一个分数 ¼
,但在 CSS 中,斜杠 /
并不是用来做除法的 。它的作用是将多个值分隔开。在这里,它的作用是:
表示从第 1 条列线(column line)开始,到第 4 条列线结束。
也就是说:这个子元素会跨越第 1~3 列,占据了三列的宽度。这是下面这种写法的简写形式:
css
.child {
grid-column-start: 1;
grid-column-end: 4;
}
注意这里的数字是基于"网格线(grid lines)"的编号,而不是"网格列(grid columns)"的编号。
👇 这个区别配合示意图会更容易理解:
译注:每一列的两侧都有"线",线的编号从
1
开始。第一列在第 1-2 条线之间,第二列在第 2-3 条线之间,以此类推。所以跨越 3 列需要从1
到4
。

这点很容易让人迷惑:一个 4 列的 Grid 实际上会有 5 条列线(column lines) 。
这是因为每一列都有一个开始线和结束线,4 列自然就需要 5 条线来分隔它们。
当我们给 Grid 的子元素设置位置时,是基于这些"列线"来锚定位置的。
✅ 举例:如果我们希望子元素横跨前 3 列,就需要它从第
1
条列线开始,到第4
条列线结束:grid-column: 1 / 4
默认情况下,在像英语这样的 从左到右(LTR)语言环境中,列线是从左向右编号的(1、2、3...)。
但我们也可以使用负数,从右向左进行编号。
css
.child {
/* 表示从右边数第二条线开始(即倒数第 2 列) */
grid-column: -2;
}
这在某些场景下非常有用,比如我们希望某个元素总是靠右对齐、但不确定总列数的时候。
🧠 正负线号混用
更妙的是,我们还可以混用正数和负数,用起来非常灵活:
css.child { /* 从第 2 列线开始,到倒数第 2 列线结束 */ grid-column: 2 / -2; }
这段代码表示:子元素横跨了中间的若干列(不包括最左和最右两列),不管总共有多少列都适配得很好。
我们可以注意到子元素虽然我们并没有修改
grid-column
的设置,它却能横跨整个网格的宽度 ! 这是因为我们将子元素设置为从 第一条列线 到 最后一条列线:
cssgrid-column: 1 / -1;
不管这个 Grid 有多少列,这个写法都能让元素横向铺满整个网格,非常方便。
📌 实战案例:这种技巧的一个典型用法可以参考我写的一篇博客
《使用 CSS Grid 实现 Full-Bleed(全宽)布局》
📐 Grid Areas(网格区域)
现在我们来聊聊 CSS Grid 最酷的一项能力之一:命名网格区域(Grid Areas) 。😄
假设我们正在构建如下的页面布局结构:
使用我们已经学到的知识,我们可以这样构建这个布局:
css
.grid {
display: grid;
grid-template-columns: 2fr 5fr;
grid-template-rows: 50px 1fr;
}
.sidebar {
grid-column: 1;
grid-row: 1 / 3;
}
.header {
grid-column: 2;
grid-row: 1;
}
.main {
grid-column: 2;
grid-row: 2;
}
这个方式是可行的,但其实还有一种更简洁且语义更强 的方式:那就是使用 grid areas(网格区域) 。
我们可以这样写:
css
.parent {
display: grid;
grid-template-columns: 2fr 5fr;
grid-template-rows: 50px 1fr;
grid-template-areas:
'sidebar header'
'sidebar main';
}
这段代码做了什么呢?
我们使用 grid-template-areas
像画 ASCII 图那样,把网格的结构"画"出来了。每一行代表一行网格,每个单词表示一个区域的名字。看起来就像是一个文字版的网格结构,非常直观。
然后,对于每个子元素,我们不再用 grid-column
和 grid-row
去定位它,而是使用 grid-area
来匹配名字即可:
css
.sidebar {
grid-area: sidebar;
}
.header {
grid-area: header;
}
.main {
grid-area: main;
}
当我们希望某个区域跨越多行或多列时,只需在模板中重复该区域名即可。比如上面 sidebar
就是跨两行的,因为它在模板的第一列出现了两次。
那么我们该用区域(area)还是行列(row/column)?
如果你在构建一个 明确的布局结构 (像上面这种 header/sidebar/main),我推荐使用 grid-area ------ 这样我们赋予了网格更强的"语义性",可读性也更高,不像纯数字那样晦涩。
不过要注意一点:grid areas 更适合固定结构的网格 (固定行列数)。如果是动态或隐式网格,还是得用 grid-row
/ grid-column
。
💡 注意键盘用户的可访问性问题**
说到这里,有一个隐藏的"陷阱":Grid 的视觉顺序不会影响键盘导航的顺序。
什么意思呢?Tab 键的焦点移动顺序,仍然是基于 DOM 中的元素顺序,而不是你通过 CSS Grid 排列出来的视觉顺序。
举个例子:

我们放了一组按钮在页面上,用 CSS Grid 排列成 5 行 5 列,肉眼看上去它们顺序是:
1 2
3 4
5
6
但是如果它们在 DOM 结构中是这样排列的:
html
<button>One</button>
<button>Four</button>
<button>Two</button>
<button>Five</button>
<button>Three</button>
<button>Six</button>
那么用户在使用键盘按 Tab
时,焦点移动顺序依然是 DOM 顺序,即:
1 ➜ 4 ➜ 2 ➜ 5 ➜ 3 ➜ 6
译者注:这儿原文有一个视频 www.joshwcomeau.com/images/inte...
可以看到,聚焦轮廓(focus outline)会在页面中跳来跳去,用户很难理解焦点到底在哪。这是因为按钮的聚焦顺序仍然基于 DOM 中的出现顺序 ,而不是它们在页面上的视觉排列。
为了修复这个问题,我们应该调整 DOM 中网格子元素的顺序,使其与视觉上的排列一致。这样,用户就能按照从左到右、从上到下的顺序正常使用 Tab 键导航。
🧭
reading-order
CSS 属性CSS 工作组意识到这是 CSS Grid 的一个缺陷 ------ 必须让 DOM 顺序和视觉顺序保持一致,这和"能自由指定子元素在哪个网格单元格"这一 Grid 的优势有点背道而驰。
一个修复方案已经在推进中:未来,浏览器可能支持一个名为
reading-order
的 CSS 属性,它能让你告诉浏览器按照视觉顺序而非 DOM 顺序来聚焦元素。不过,截至 2024 年 9 月 ,还没有任何浏览器实现 这个属性。你可以在 Rachel Andrew 的文章《解决 CSS 布局与源码顺序的脱节问题》 中了解更多细节。
📐 对齐方式(Alignment)
到目前为止,我们示例中的所有行和列都会自动拉伸以填满整个网格容器。但实际上,这并不是必须的!
比如,如果我们定义了两个宽度都是 90px
的列,而父容器宽度大于 180px
,那么在网格的右边就会出现一些空白区域:

我们可以使用 justify-content
属性来控制列在水平方向上的分布方式

如果你熟悉 Flexbox 布局算法,那么你可能觉得这些对齐属性非常眼熟。实际上,CSS Grid 在 Flexbox 首次引入的对齐属性基础上,做了进一步的扩展。
不过这里有一个关键的区别:
我们现在对齐的是列(或行)本身,而不是网格项(items)!
也就是说,justify-content
是用来控制整个网格的"间隔盒子"(compartments)如何分布的,而不是控制每个具体子项的位置。
如果我们希望控制网格项本身在每个格子里的位置,就需要使用justify-items
属性:
(编者注:这儿是互动式demo,再次强烈大家点进原文试用~)
当我们将一个 DOM 节点放进一个 grid 容器中时,默认行为是:
该节点会横向拉伸,占满整个列的宽度。
这种行为其实类似于传统的流式布局(Flow layout)中,<div>
元素默认会横向撑满它的父容器。但有了 justify-items
,我们可以自定义这种行为:它让我们能跳脱对称列宽的限制,更加灵活地控制布局样式。
当我们把 justify-items
设置为非 stretch
时,子元素就会按其内容尺寸决定宽度,不会强行拉伸。结果是:同一列中的不同项可以有不同的宽度,看起来更自然也更灵活。
如果你只想某一个 grid 子元素的对齐方式不同 ,我们甚至可以使用justify-self
控制单个子项对齐:
与
justify-items
不同,justify-self
是设置在子项上的 ,它用来控制单个 grid 子元素在单元格内的水平对齐方式。
我们可以这样理解:
justify-items
是在父元素上设置的,它为所有 grid 子元素设定了一个默认的justify-self
。
🎯 对齐行(Aligning rows)
前面我们一直在讨论水平方向(横向) 的对齐方式,现在我们来看看 垂直方向(纵向) 的对齐。 CSS Grid 提供了另一套对齐属性,用于控制垂直方向的对齐:
align-content
就像 justify-content
,但它作用于行 ,而不是列。 同理,align-items
就像 justify-items
,但它控制的是子元素在网格区域中的垂直对齐方式,而不是水平方向。 我们可以进一步总结如下:
属性前缀 | 对象 | 方向 |
---|---|---|
justify- |
控制 列(columns) | 水平方向 |
align- |
控制 行(rows) | 垂直方向 |
再往下细分:
属性 | 作用范围 | 控制内容 |
---|---|---|
*-content |
整个网格结构 | 控制整体网格布局的对齐方式 |
*-items |
所有 grid 子项 | 控制每个子项在单元格中的对齐方式 |
*-self |
单个 grid 子项 | 控制某个子项在单元格中的对齐方式 |
🎯 双轴居中技巧(Two-line centering trick)
这是作者最喜欢的 CSS Grid 小技巧之一:
只用两行 CSS,就可以让某个元素在容器中水平和垂直居中:
place-content
是一个简写属性,相当于同时设置:
css
.parent {
justify-content: center;
align-content: center;
}
我们之前学到:
justify-content
控制 列的对齐方式(横向)align-content
控制 行的对齐方式(纵向)
那么 place-content: center
的效果就是:
✅ 将网格的行与列都居中对齐
在这个例子中,我们使用了一个隐式网格(implicit grid),里面只有一个子元素,所以其实就是一个 1 × 1
的网格。 使用 place-content: center
,就是把这一个单元格定位在整个容器的中心位置。
💡 总结:
虽然现代 CSS 提供了很多居中元素的方式,比如的(这部分内容是译者补充):
flex + justify-content/align-items
grid + place-items
position: absolute + transform
margin: auto
text-align/line-height
(早期用法)
但要做到只写两行 CSS 、而且不管容器尺寸、子项尺寸都能完美双轴居中的,目前最优雅、最简洁的方式可能就是:
css
.parent {
display: grid;
place-content: center;
}
冰山一角 💡
在这篇教程中,我们已经学习了 CSS Grid 布局算法中一些最基础、最核心的内容 。 但说实话,这些只是冰山一角,还有大量高级用法和技巧 我们还没涉及! 如果你觉得这篇文章对你有帮助,那你也许会对我整理的一份更系统、更深入的学习资源感兴趣。
它的名字叫:CSS for JavaScript Developers
译者注:剩下的内容是介绍这个课程的相关内容,感兴趣的同学可以自己去看哦,就不在这里搬运啦