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

要点

如果您熟悉 Flexbox,那么 Grid 应该会给您一种熟悉的感觉。Rachel Andrew 维护了一个非常棒的专门介绍 CSS 网格的网站,可帮助您上手使用。网格模式现已在 Google Chrome 中推出。

Flexbox?网格?

在过去几年里,CSS Flexbox 已被广泛使用,并且浏览器支持情况非常理想(除非您是必须支持 IE9 及更低版本的倒霉之人)。Flexbox 让许多复杂的布局任务变得更轻松,例如元素之间的等距间距、从上到下的布局,或 CSS 魔法中的圣杯:垂直居中。

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

但遗憾的是,屏幕通常还有一个需要我们关注的维度。很遗憾,仅使用 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 整除,因此适用于许多设计。我们来实现以下布局:

无法跨多个 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 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-widthmax-width 合二为一。)如前所述,最小宽度为 200 像素。宽度上限为 3frfr 是网格专用单位,可让您将可用空间分配给网格元素。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 中默认启用了网格,因此您可以立即开始使用。