要点
如果您熟悉 Flexbox,会觉得 Grid 十分熟悉。 Rachel Andrew 维护着一个专门介绍 CSS 网格的网站,以帮助您入门。Google Chrome 浏览器现已推出网格功能。
Flexbox?网格?
在过去的几年里,CSS Flexbox 得到广泛使用,浏览器支持也非常好(除非您是一群弱者,因此不得不支持 IE9 及更低版本)。Flexbox 可以简化许多复杂的布局任务,例如元素之间等距的间距、自上而下的布局或者 CSS 魔法水准的“垂直居中”。
但唉,屏幕通常还有一个我们需要关注的维度。 你不必自行调整元素大小,可惜的是,你不可能只使用 Flexbox,就不能同时兼顾垂直和水平节奏。这正是 CSS 网格的用武之地。
CSS Grid 一直处于开发阶段,在大多数浏览器中,该标志已有超过 5 年的时间。我们还在开发过程中投入了额外的时间来研究互操作性,以避免像 Flexbox 那样因启动问题而出错。因此,如果您使用 Grid 在 Chrome 中实现布局,可能会在 Firefox 和 Safari 中得到相同的结果。在撰写本文时,Microsoft Edge 的 Grid 实现已过时(与 IE11 中已有的版本相同),并且“正在考虑”更新。
尽管在概念和语法上相似,但不要将 Flexbox 和 Grid 视为互相竞争的布局技术。Grid 采用二维布局,而 Flexbox 则采用一维布局。结合使用这两者会产生协同效应。
定义网格
为了熟悉网格的各个属性,我强烈建议您参考 Rachel Andrew 的 Grid By Example 或 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 网格。太棒了!
下一步是实现网格的行和列。我们可以在模型中实现全部 12 列,但由于我们并未使用每一列,因此这样做会导致 CSS 显得杂乱。为简单起见,我们将按以下方式实现布局:
页眉和页脚的宽度是可变的,而内容在两个尺寸上是可变的。导航栏在两个维度上也是可变的,但我们要求其最小宽度为 200px。(为什么?当然是为了展现 CSS 网格的功能)
在 CSS 网格中,这组列和行称为轨道。我们先来定义第一组轨道,即行:
body {
display: grid;
grid-template-rows: 150px auto 100px;
}
grid-template-rows
采用定义各行的大小序列。在本例中,我们将第一行的高度设置为 150 像素,将最后一行的高度设置为 100 像素。中间行设置为 auto
,这意味着它将调整到所需的高度,以容纳该行中的网格项(网格容器的子级)。由于正文是拉伸至整个视口的,因此包含内容的轨道(上图中的黄色部分)至少会填满所有可用空间,但会在必要时扩展(并使文档滚动)。
对于列,我们想要采用更具动态性的方法:我们希望 Nav 和内容都能够增大(和缩小),但我们希望 nav 无论缩小到 200px 以下,同时希望内容大于 nav。在 Flexbox 中,我们会使用 flex-grow 和 flex- Floodlight,但在 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 像素)。
虽然网格项的放置位置尚不正确,但行和列的 size 行为正常,并且产生了我们想要的行为:
放置物品
Grid 最强大的功能之一是能够在不考虑 DOM 顺序的情况下放置列表项。(不过,由于屏幕阅读器会在 DOM 中导航,因此我们强烈建议您查看如何对元素重新排序,以便能够正常访问这些元素。)如果没有手动放置,则系统会按 DOM 顺序将元素放置在网格中,即从左到右、从上到下排列。每个元素占用一个单元格。可以使用 grid-auto-flow
更改网格的填充顺序。
那么,该如何放置元素呢?可以说,放置网格项的最简单方法是定义网格项覆盖的列和行。Grid 提供两种语法来实现此目的:在第一种语法中,您需要定义起点和终点。在第二个代码中,您需要定义起点和 span:
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 简化网页设计。她甚至围绕这本书写了一本书。她的网站 Grid By Example 是一项非常实用的资源,可以帮助您熟悉 Grid。许多人认为 Grid 是网页设计的革命性举措,它现已在 Chrome 中默认处于启用状态,因此您可以立即开始使用它。