CSS 网格布局

CSS 网格布局是一种用于 Web 的二维布局系统。它允许您将内容组织成行和列,并提供了许多功能来简化复杂布局的创建。本文将解释您开始使用网格布局所需了解的所有内容。

预备知识 使用 HTML 构造内容CSS 样式基础文本和字体样式基础,熟悉CSS 布局基本概念
学习成果
  • 了解 CSS 网格的目的——以二维方式灵活地布局一组块级或行内元素。
  • 了解网格术语——行、列、间隙和槽沟。
  • 了解 display: grid 默认提供的内容。
  • 定义网格行、列和间隙。
  • 在网格上定位元素。

什么是网格布局?

网格是一系列水平线和垂直线的集合,它们创建了一个模式,我们可以根据该模式排列我们的设计元素。它们帮助我们创建布局,使我们的元素在页面之间移动时不会跳动或改变宽度,从而为我们的网站提供更大的一致性。

网格通常有,以及每行和每列之间的间隙。这些间隙通常被称为槽沟

CSS grid with parts labelled as rows, columns and gutters. Rows are the horizontal segments of the grid and Columns are the vertical segments of the grid. The space between two rows is called as 'row gutter' and the space between 2 columns is called as 'column gutter'.

在 CSS 中创建网格

确定设计所需的网格后,您可以使用 CSS 网格布局来创建它。我们首先将了解网格布局的基本功能,然后探讨如何为您的项目创建一个简单的网格系统。下面的视频提供了使用 CSS 网格的良好视觉解释

定义网格

让我们试试网格布局,这里有一个带容器的示例,其中包含一些子项。默认情况下,这些项以正常流显示,导致它们一个接一个地出现。

html
<div class="container">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
</div>
css
body {
  font-family: sans-serif;
}
.container > div {
  border-radius: 5px;
  padding: 10px;
  background-color: rgb(207 232 220);
  border: 2px solid rgb(79 185 227);
}

与定义 flexbox 类似,您通过将 display 属性的值设置为 grid 来定义网格布局。与 flexbox 的情况一样,display: grid 属性将容器的所有直接子项转换为网格项。我们已将以下 CSS 添加到文件中

css
.container {
  display: grid;
}

与 flexbox 不同,这些项不会立即看起来有所不同。声明 display: grid 会为您提供一个一列网格,因此您的项将像在正常流中一样继续一个接一个地显示。

为了看到更像网格的东西,我们需要向网格添加一些列。让我们添加三列 200 像素的列。您可以使用任何长度单位或百分比来创建这些列轨道。

css
.container {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}

您应该会看到这些项已重新排列,以便网格的每个单元格中都有一个。

使用 fr 单位的灵活网格

除了使用长度和百分比创建网格之外,我们还可以使用 frfr 单位表示网格容器中可用空间的一个分数,用于灵活调整网格行和列的大小。

在这里,我们将轨道列表更改为以下定义,创建三个 1fr 轨道

css
.container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

您现在拥有灵活的轨道。fr 单位按比例分配空间,因此您可以为您的轨道指定不同的正值。将您的轨道列表更改为以下定义,创建一个 2fr 轨道和两个 1fr 轨道

css
.container {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
}

第一个轨道获得可用空间的 2fr,而其他两个轨道获得 1fr,使第一个轨道更大。您可以将 fr 单位与固定长度单位混合使用。在这种情况下,首先用完固定轨道所需的空间,然后将剩余空间分配给其他轨道。

注意: fr 单位分配的是可用空间,而不是所有空间。因此,如果您的一个轨道内部有较大的内容,则可共享的可用空间会更少。

轨道之间的间隙

为了在轨道之间创建间隙,我们使用以下属性

在这里,我们添加 gap 属性以创建轨道之间的间隙,值为 20px

css
.container {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 20px;
}

这些间隙可以是任何长度单位或百分比,但不能是 fr 单位。

重复轨道列表

您可以使用 CSS repeat() 函数重复您的整个轨道列表或仅其一部分。在这里,我们将轨道列表更改为以下内容

css
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

您现在将得到三个 1fr 轨道,就像以前一样。传递给 repeat() 函数的第一个值指定您希望列表重复的次数,而第二个值是一个轨道列表,可以是您希望重复的一个或多个轨道。

隐式网格和显式网格

到目前为止,我们只指定了列轨道,但会自动创建行以容纳内容。这个概念突出了显式网格和隐式网格之间的区别。以下是关于这两种网格之间区别的更多信息

  • 显式网格使用 grid-template-columnsgrid-template-rows 创建。
  • 当内容放置在该网格之外时(例如,通过绘制额外的网格线放置到行中),隐式网格会扩展已定义的显式网格。

默认情况下,隐式网格中创建的轨道是 auto 大小,这通常意味着它们足够大以包含其内容。如果您希望为隐式网格轨道指定大小,可以使用 grid-auto-rowsgrid-auto-columns 属性。如果将 grid-auto-rows 和值 100px 添加到 CSS 中,您会看到那些创建的行现在高 100 像素。

css
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
  gap: 20px;
}

minmax() 函数

如果我们在这些 100 像素高的轨道中添加高于 100 像素的内容,那么这些轨道将不会非常有用,在这种情况下会导致溢出。最好让轨道至少高 100 像素,并且如果添加更多内容仍然可以扩展。关于 Web 的一个相当基本的事实是,您永远不知道某个东西会有多高——额外的内容或更大的字体大小可能会导致在每个维度都试图做到像素级完美的设计出现问题。

minmax() 函数允许我们为轨道设置最小和最大尺寸,例如 minmax(100px, auto)。最小尺寸为 100 像素,但最大尺寸为 auto,它将扩展以适应更多内容。在这里,我们将 grid-auto-rows 更改为使用 minmax()

css
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(50px, auto);
  gap: 20px;
}

如果您添加额外内容,您会看到轨道会扩展以适应内容。请注意,扩展发生在行中。

尽可能多的列

我们可以结合我们所学的关于轨道列表、重复符号和 minmax() 的一些知识来创建有用的模式。有时,能够让网格创建尽可能多的列以适应容器是很有帮助的。我们通过使用 repeat() 函数设置 grid-template-columns 的值来实现这一点,但不是传入数字,而是传入关键字 auto-fit。对于函数的第二个参数,我们使用 minmax(),其最小值为我们想要的最小轨道大小,最大值为 1fr

css
.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  grid-auto-rows: minmax(50px, auto);
  gap: 20px;
}

这之所以有效,是因为网格会创建尽可能多的 230 像素列以适应容器,然后将剩余空间分配给所有列。最大值是 1fr,我们已经知道,它在轨道之间均匀分配空间。

基于线的放置

现在我们从创建网格转到在网格上放置内容。我们的网格总是有线——这些线从 1 开始编号,并与文档的书写模式相关。例如,在英语(从左到右书写)中,列线 1 将在网格的左侧,行线 1 在顶部,而在阿拉伯语(从右到左书写)中,列线 1 将在右侧。

为了沿这些线定位项目,我们可以指定项目应放置的网格区域的起始线和结束线。我们可以使用以下四个属性来完成此操作

这些属性接受行号作为它们的值,因此我们可以指定一个项目应该从第 1 行开始并在第 3 行结束,例如。另外,您还可以使用速记属性,允许您同时指定起始线和结束线,用正斜杠 / 分隔

  • grid-columngrid-column-startgrid-column-end 的简写
  • grid-rowgrid-row-startgrid-row-end 的简写
html
<div class="container">
  <header>Header</header>
  <main>
    <h1>Main</h1>
    <p>Main content…</p>
  </main>
  <aside>
    <h2>Aside</h2>
    <p>Related content</p>
  </aside>
  <footer>footer</footer>
</div>
css
.container {
  font-family: sans-serif;
  display: grid;
  grid-template-columns: 1fr 3fr;
  gap: 20px;
}
header,
footer {
  border-radius: 5px;
  padding: 10px;
  background-color: rebeccapurple;
  color: whitesmoke;
  text-align: center;
}
aside {
  border-right: 1px solid rebeccapurple;
}

在没有定义位置的情况下,您可以看到自动放置将每个项目放置在网格中的自己的单元格中。<header> 占据了 1fr (四分之一),<main> 占据了 3fr (四分之三)。

让我们使用网格线排列我们网站的所有元素。将以下规则添加到 CSS 的底部

css
header {
  grid-column: 1 / 3;
  grid-row: 1;
}
main {
  grid-column: 2;
  grid-row: 2;
}
aside {
  grid-column: 1;
  grid-row: 2;
}
footer {
  grid-column: 1 / 3;
  grid-row: 3;
}

现在 <header><footer> 设置为 1 / 3,这意味着从第 1 行开始,到第 3 行结束。

注意:您还可以使用值 -1 来定位结束列或行线,然后使用负值从末尾向内计数。另请注意,行始终从显式网格的边缘开始计数,而不是隐式网格

使用 grid-template-areas 进行定位

另一种在网格上排列项目的方法是使用 grid-template-areas 属性并为设计的各个元素命名。

css
.container {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar content"
    "footer footer";
  grid-template-columns: 1fr 3fr;
  gap: 20px;
}
header {
  grid-area: header;
}
main {
  grid-area: content;
}
aside {
  grid-area: sidebar;
}
footer {
  grid-area: footer;
}

在这里,我们使用 grid-template-areas 属性来定义 3 行的布局方式。第一行值为 header header,第二行值为 sidebar content,第三行值为 footer footer。然后我们使用 grid-area 属性来定义元素在 grid-template-areas 中的位置。

grid-template-areas 的规则如下

  • 您需要填充网格的每个单元格。
  • 要跨越两个单元格,请重复名称。
  • 要留空单元格,请使用 .(句点)。
  • 区域必须是矩形——例如,您不能有 L 形区域。
  • 区域不能在不同位置重复。

您可以调整我们的布局,将页脚只放在文章下方,并将侧边栏向下延伸。这是一种非常好的描述布局的方式,因为只需查看 CSS 即可清楚地了解发生了什么。

嵌套网格和子网格

可以在另一个网格中嵌套网格,创建 “子网格”。您可以通过在父网格中的项目上设置 display: grid 属性来实现此目的。

让我们通过添加文章容器并使用嵌套网格来控制多篇文章的布局来扩展前面的示例。虽然我们在嵌套网格中只使用一列,但我们可以使用 grid-template-rows 属性将行定义为按 4:3:3 的比例分割。这种方法允许我们创建一个布局,其中页面顶部的一篇文章具有大的显示,而其他文章具有较小的预览式布局。

css
main {
  grid-area: content;
  display: grid;
  grid-template-rows: 4fr 3fr 3fr;
  gap: inherit;
}
article {
  padding: 10px;
  border: 2px solid rebeccapurple;
  border-radius: 5px;
}

为了更轻松地在嵌套网格中使用布局,您可以在 grid-template-rowsgrid-template-columns 属性上使用 subgrid。这允许您利用父网格中定义的轨道。

在下面的示例中,我们使用基于线的定位,使嵌套网格能够跨越父网格的多个列和行。我们添加了 subgrid 来继承父网格的列轨道,同时在嵌套网格中为行添加了不同的布局。

css
.container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(1, 1fr);
  gap: 10px;
}
.subgrid {
  grid-column: 1 / 4;
  grid-row: 2 / 4;
  display: grid;
  gap: inherit;
  grid-template-columns: subgrid;
  grid-template-rows: 2fr 1fr;
}

网格框架

有许多网格框架可用,提供 12 或 16 列网格,以帮助您布局内容。好消息是,您可能不需要任何第三方框架来帮助您创建基于网格的布局——网格功能已包含在规范中,并受到大多数现代浏览器的支持。

这有一个定义了 12 列网格的容器,使用 grid-template-columns: repeat(12, 1fr);,以及我们在前两个示例中使用的相同标记。我们现在可以使用基于线的定位将内容放置在 12 列网格上。

css
.container {
  font-family: sans-serif;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
}
header {
  grid-column: 1 / 13;
  grid-row: 1;
}
main {
  grid-column: 4 / 13;
  grid-row: 2;
}
aside {
  grid-column: 1 / 4;
  grid-row: 2;
}
footer {
  grid-column: 1 / 13;
  grid-row: 3;
}

如果您使用 Firefox 网格检查器在您的设计上叠加网格线,您可以看到我们的 12 列网格是如何工作的。

A 12 column grid overlaid on our design.

总结

在本概述中,我们遍历了 CSS 网格布局的主要功能。您应该能够开始在您的设计中使用它。

在下一篇文章中,我们将为您提供一些测试,您可以使用它们来检查您对所有这些信息的理解和掌握程度。

另见

CSS 网格布局

主要的 CSS 网格布局模块页面,包含许多其他资源

CSS 网格完整指南

CSS-Tricks 上的视觉指南 (2023)。

Grid Garden

一个教育游戏,可在 cssgridgarden.com 上学习和更好地理解网格基础知识。