要点
如果您熟悉 Flexbox,那么 Grid 应该会给您一种熟悉的感觉。Rachel Andrew 维护了一个非常棒的专门介绍 CSS 网格的网站,可帮助您上手使用。网格模式现已在 Google Chrome 中推出。
Flexbox?网格?
在过去几年里,CSS Flexbox 已被广泛使用,并且浏览器支持情况非常理想(除非您是必须支持 IE9 及更低版本的倒霉之人)。Flexbox 让许多复杂的布局任务变得更轻松,例如元素之间的等距间距、从上到下的布局,或 CSS 魔法中的圣杯:垂直居中。
但遗憾的是,屏幕通常还有一个需要我们关注的维度。很遗憾,仅使用 Flexbox 无法同时实现纵向和横向节奏,除非您自行处理元素的大小。这时,CSS 网格就派上用场了。
CSS Grid 在大多数浏览器中已开发并通过标志启用超过 5 年,我们还花了额外的时间来实现互操作性,以避免像 Flexbox 那样在发布时出现 bug。因此,如果您在 Chrome 中使用网格布局来实现布局,那么在 Firefox 和 Safari 中获得相同结果的可能性很大。在撰写本文时,Microsoft Edge 中的 Grid 实现已过时(与 IE11 中已有的实现相同),并且更新“正在考虑”。
尽管 Flexbox 和 Grid 在概念和语法上有相似之处,但请不要将它们视为竞争的布局技术。网格在两个维度上进行排列,而 Flexbox 在一个维度上进行排列。将这两者搭配使用可以产生协同效应。
定义网格
如需熟悉 Grid 的各个属性,我强烈建议您阅读 Rachel Andrew 的《Grid By Example》(通过示例了解 Grid)或 CSS Tricks 的备忘单。如果您熟悉 Flexbox,那么很多属性及其含义应该会很熟悉。
我们来看看标准的 12 列网格布局。经典的 12 列布局很受欢迎,因为 12 可以被 2、3、4 和 6 整除,因此适用于许多设计。我们来实现以下布局:
我们先从标记代码开始:
<!DOCTYPE html>
<body>
<header></header>
<nav></nav>
<main></main>
<footer></footer>
</body>
在样式表中,我们首先扩展 body
,使其覆盖整个视口并将其转换为网格容器:
html, body {
width: 100vw;
min-height: 100vh;
margin: 0;
padding: 0;
}
body {
display: grid;
}
现在,我们使用的是 CSS Grid。太棒了!
下一步是实现网格的行和列。我们可以在模拟中实现所有 12 个列,但由于我们并非要使用所有列,这样做会使 CSS 不必要地变得杂乱无序。为简单起见,我们将以这种方式实现布局:
页眉和页脚的宽度可变,内容的宽度和高度可变。导航栏的两个尺寸也将是可变的,但我们将强制将其最小宽度设为 200 像素。(为什么?当然,是为了展示 CSS Grid 的功能。)
在 CSS 网格中,一组列和行称为“轨道”。我们先来定义第一组轨道(即行):
body {
display: grid;
grid-template-rows: 150px auto 100px;
}
grid-template-rows
接受用于定义各行大小的序列。在本例中,我们将第一行的高度设置为 150px,最后一行的高度设置为 100px。中间行设置为 auto
,这意味着它会调整为所需高度,以容纳该行中的网格项(网格容器的子项)。由于我们的正文会延伸到整个视口,因此包含内容的轨道(上图中的黄色部分)至少会填满所有可用空间,但在必要时也会扩大(并使文档滚动)。
对于列,我们希望采用更具动态性的方法:我们希望导航栏和内容都能够放大(和缩小),但导航栏绝不能缩小到 200 像素以下,并且内容要比导航栏大。在 Flexbox 中,我们会使用 flex-grow 和 flex-shrink,但在 Grid 中,情况略有不同:
body {
display: grid;
grid-template-rows: 150px auto 100px;
grid-template-columns: minmax(200px, 3fr) 9fr;
}
我们将定义 2 列。第一个列使用 minmax()
函数进行定义,该函数接受 2 个值:相应轨道的最小尺寸和最大尺寸。(就像 min-width
和 max-width
合二为一。)如前所述,最小宽度为 200 像素。宽度上限为 3fr
。fr
是网格专用单位,可让您将可用空间分配给网格元素。fr 可能代表“小数单位”,但很快也可能会代表“自由单位”。此处的值表示这两个列都会扩展到填满屏幕,但内容列的宽度始终是导航列的 3 倍(前提是导航列的宽度始终保持大于 200 像素)。
虽然网格项的放置方式尚不正确,但行和列的大小行为正确无误,并产生了我们期望的行为:
放置物品
Grid 最强大的功能之一是能够在不考虑 DOM 顺序的情况下放置项。(不过,由于屏幕阅读器会浏览 DOM,因此我们强烈建议您注意元素的重新排序方式,以便正确实现无障碍功能。)如果未手动放置,系统会按 DOM 顺序将元素放置在网格中,并从左到右、从上到下进行排列。每个元素占用一个单元格。您可以使用 grid-auto-flow
更改填充网格的顺序。
那么,我们如何放置元素?可以说,放置网格项的最简单方法是定义它们涵盖的列和行。Grid 提供了两种语法来实现此目的:在第一个语法中,您可以定义起点和终点。在第二个中,您可以定义起始点和跨度:
header {
grid-column: 1 / 3;
}
nav {
grid-row: 2 / span 2;
}
我们希望标题从第一列开始,在第三列之前结束。 导航栏应从第二行开始,总共跨 2 行。
从技术层面来说,我们已经完成了布局的实现,但我想向您展示 Grid 提供的一些便捷功能,以便您更轻松地进行放置。第一种功能是,您可以为轨道边框命名,并使用这些名称进行放置:
body {
display: grid;
grid-template-rows: 150px [nav-start] auto 100px [nav-end];
grid-template-columns: [header-start] minmax(200px, 3fr) 9fr [header-end];
}
header {
grid-column: header-start / header-end;
}
nav {
grid-row: nav-start / nav-end;
}
上述代码将生成与上一个代码相同的布局。
更强大的功能是,您可以在网格中为整个区域命名:
body {
display: grid;
grid-template-rows: 150px auto 100px;
grid-template-columns: minmax(200px, 3fr) 9fr;
grid-template-areas: "header header"
"nav content"
"nav footer";
}
header {
grid-area: header;
}
nav {
grid-area: nav;
}
grid-template-areas
接受由空格分隔的名称字符串,让开发者可以为每个单元格指定名称。如果两个相邻单元格具有相同的名称,则它们将合并到同一区域。这样,您就可以为布局代码提供更多语义,并使媒体查询更直观。同样,此代码将生成与之前相同的布局。
还有其他问题吗?
是的,是的,有太多内容无法在一篇博文中介绍完。Rachel Andrew 也是一名 GDE,是 CSS 工作组的受邀专家,从一开始就与他们合作,确保 Grid 能够简化 Web 设计。她甚至还写了一本书。她的网站 Grid By Example 是熟悉 Grid 的宝贵资源。许多人认为网格是 Web 设计领域的一次革命性飞跃,现在,Chrome 中默认启用了网格,因此您可以立即开始使用。