CSS 网格 - 表格布局恢复正常。立足于正方形

要点

如果您熟悉 Flexbox,会觉得 Grid 十分熟悉。 Rachel Andrew 维护着一个专门介绍 CSS 网格的网站,以帮助您入门。Google Chrome 浏览器现已推出网格功能。

Flexbox?网格?

在过去的几年里,CSS Flexbox 得到广泛使用,浏览器支持也非常好(除非您是一群弱者,因此不得不支持 IE9 及更低版本)。Flexbox 可以简化许多复杂的布局任务,例如元素之间等距的间距、自上而下的布局或者 CSS 魔法水准的“垂直居中”。

您无法跨多个 Flexbox 容器对齐元素。

但唉,屏幕通常还有一个我们需要关注的维度。 你不必自行调整元素大小,可惜的是,你不可能只使用 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 整除,因此对许多设计都很有用。我们来实现此布局:

您无法跨多个 Flexbox 容器对齐元素。

我们先从标记代码开始:

<!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-widthmax-width 合二为一。)正如我们之前讨论的那样,最小宽度为 200 像素。宽度上限为 3frfr 是一个网格专用单位,可让您将可用空间分配给网格元素。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 中默认处于启用状态,因此您可以立即开始使用它。