网格布局的基本概念

CSS 网格布局 将二维网格系统引入 CSS。网格可用于布局主要页面区域或小型用户界面元素。本文介绍了 CSS 网格布局以及 CSS 网格布局级别 1 规范中包含的新术语。本概述中显示的功能将在本指南的其余部分中进行更详细的解释。

什么是网格?

网格是一组相交的水平线和垂直线,定义了列和行。元素可以放置在这些列和行线内的网格上。CSS 网格布局具有以下功能

固定和灵活的轨道大小

您可以使用固定轨道大小创建网格,例如使用像素。这将网格设置为指定的像素,以适应您所需的布局。您还可以使用百分比或专为此目的设计的 fr 单位创建使用灵活大小的网格。

项目放置

您可以使用行号、名称或通过定位网格的某个区域将项目放置到网格上的精确位置。网格还包含一个算法来控制未在网格上指定显式位置的项目的放置。

创建额外的轨道来容纳内容

您可以使用网格布局定义显式网格。网格布局规范足够灵活,可以在需要时添加额外的行和列。其中包括添加“尽可能多地适合容器的列”等功能。

对齐控制

网格包含对齐功能,因此我们可以控制项目放置到网格区域后如何对齐,以及整个网格如何对齐。

重叠内容的控制

可以将多个项目放置到网格单元格或区域中,并且它们可以彼此部分重叠。然后可以使用 z-index 属性控制此分层。

网格是一个功能强大的规范,当与 CSS 的其他部分(如 Flexbox)结合使用时,可以帮助您创建以前无法在 CSS 中构建的布局。这一切都始于在您的 网格容器中创建网格。

网格容器

我们通过在元素上声明 display: griddisplay: inline-grid 来创建网格容器。一旦我们这样做,该元素的所有直接子元素都将成为网格项目

在此示例中,我有一个名为 wrapper 的包含 div,其中包含五个子元素。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>

我将 .wrapper 设为网格容器。

css
.wrapper {
  display: grid;
}

现在,所有直接子元素都变成了网格项目。在网页浏览器中,将这些项目变成网格之前,它们的显示方式没有任何区别,因为网格已经为这些项目创建了一个单列网格。此时,您可能会发现使用 Firefox 开发者工具中提供的 网格检查器 会很有帮助。如果您在 Firefox 中查看此示例并检查网格,您将在值 grid 旁边看到一个小图标。单击它,然后网格将覆盖在浏览器窗口中。

Using the grid highlighter in DevTools to view a grid

在您学习并使用 CSS 网格布局时,此工具将让您更好地了解网格的视觉效果。

如果我们想让它更像网格,我们需要添加列轨道。

网格轨道

我们使用 grid-template-rowsgrid-template-columns 属性在网格上定义行和列。这些定义了网格轨道。网格轨道是网格上任意两条相邻线之间的空间。下图显示了一个突出显示的轨道——这是我们网格中的第一行轨道。

A box with 3 grid items. Above the three items is a solid light green area which is the track.

网格轨道在显式网格中使用 grid-template-columnsgrid-template-rows 属性或简写 gridgrid-template 属性定义。网格轨道也在隐式网格中创建,方法是将网格项目放置在显式网格中创建的轨道之外。

基本示例

我可以通过添加 grid-template-columns 属性并定义列轨道的尺寸来扩展我们之前的示例。

我现在创建了一个包含三个宽度为 200 像素的列轨道的网格。子项目将在此网格上布局,每个项目位于一个网格单元格中。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}

fr 单位

可以使用任何长度单位定义轨道。网格还引入了额外的长度单位来帮助我们创建灵活的网格轨道。新的 fr 单位表示网格容器中可用空间的一部分。下一个网格定义将创建三个等宽的轨道,这些轨道会根据可用空间的大小进行伸缩。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

不等尺寸

在此下一个示例中,我们创建了一个定义,其中包含一个 2fr 轨道和两个 1fr 轨道。可用空间被分成四部分。两部分分配给第一条轨道,其余两部分分别分配给接下来的两条轨道。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
}

混合灵活和绝对尺寸

在此最后一个示例中,我们将绝对尺寸轨道与 fr 单位混合使用。第一条轨道为 500 像素,因此固定宽度将从可用空间中减去。剩余的空间将分成三部分,并根据两条灵活轨道的比例分配。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: 500px 1fr 2fr;
}

使用 repeat() 表示法的轨道列表

包含许多轨道的较大网格可以使用 repeat() 表示法来重复所有或一部分轨道列表。例如,网格定义

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

也可以写成

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

重复表示法可用于轨道列表的一部分。在下一个示例中,我创建了一个网格,它包含一个初始的 20 像素轨道,然后是 6 个 1fr 轨道的重复部分,最后是一个 20 像素轨道。

css
.wrapper {
  display: grid;
  grid-template-columns: 20px repeat(6, 1fr) 20px;
}

重复表示法采用轨道列表,并使用它创建重复的轨道模式。在此下一个示例中,我的网格将包含 10 个轨道,一个 1fr 轨道,然后是一个 2fr 轨道。此模式将重复五次。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(5, 1fr 2fr);
}

隐式和显式网格

在创建我们的示例网格时,我们使用 grid-template-columns 属性专门定义了我们的列轨道,但网格也自行创建了行。这些行是隐式网格的一部分。而显式网格由使用 grid-template-columnsgrid-template-rows 定义的任何行和列组成。

如果您将某个元素放置在定义的网格之外(或由于内容量,需要更多网格轨道),则网格会在隐式网格中创建行和列。默认情况下,这些轨道的尺寸将自动调整,其尺寸将基于其中包含的内容。

您还可以使用 grid-auto-rowsgrid-auto-columns 属性为隐式网格中创建的轨道定义一组尺寸。

在下面的示例中,我们使用 grid-auto-rows 来确保在隐式网格中创建的轨道高度为 200 像素。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 200px;
}

轨道大小调整和 minmax

在设置显式网格或定义自动创建的行或列的大小调整时,我们可能希望为轨道指定最小尺寸,但也确保它们可以扩展以适应添加的任何内容。例如,我可能希望我的行永远不会收缩到小于 100 像素,但如果我的内容高度扩展到 300 像素,那么我希望行扩展到该高度。

网格为此提供了一个解决方案,即 minmax() 函数。在此下一个示例中,我在 grid-auto-rows 的值中使用 minmax()。这意味着自动创建的行将至少为 100 像素高,最大为 auto。使用 auto 表示尺寸将查看内容尺寸,并将扩展以提供空间容纳此行中单元格中最高的项目。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(100px, auto);
}
html
<div class="wrapper">
  <div>One</div>
  <div>
    Two
    <p>I have some more content in.</p>
    <p>This makes me taller than 100 pixels.</p>
  </div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>

网格线

需要注意的是,当我们定义网格时,我们定义的是网格轨道,而不是线条。然后,网格为我们提供了编号的线条,以便在放置项目时使用。在我们的三列两行网格中,我们有四条列线。

Diagram showing numbered grid lines.

线条的编号根据文档的书写模式进行。在从左到右的语言中,第 1 行位于网格的左侧。在从右到左的语言中,它位于网格的右侧。线条也可以命名,我们将在本系列的后续指南中介绍如何执行此操作。

根据线条定位项目

我们将在后续文章中详细探讨基于线条的放置。以下示例演示了以简单的方式执行此操作。放置项目时,我们以线条为目标,而不是轨道。

在以下示例中,我使用 grid-column-startgrid-column-endgrid-row-startgrid-row-end 属性在我们的三列轨道网格上放置前两个项目。从左到右,第一个项目放置在第 1 列线上,并跨越到第 4 列线,在本例中,它是网格的最右侧线。它从第 1 行线开始,到第 3 行线结束,因此跨越了两行轨道。

第二个项目从第 1 列线开始,并跨越一个轨道。这是默认设置,因此我不需要指定结束线。它还跨越了从第 3 行线到第 5 行线的两个行轨道。其他项目将自动放置到网格上的空闲空间中。

html
<div class="wrapper">
  <div class="box1">One</div>
  <div class="box2">Two</div>
  <div class="box3">Three</div>
  <div class="box4">Four</div>
  <div class="box5">Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}

.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}

.box2 {
  grid-column-start: 1;
  grid-row-start: 3;
  grid-row-end: 5;
}

注意:不要忘记,您可以在 Firefox 开发者工具中使用 网格检查器 来查看项目是如何相对于网格线条定位的。

线条定位简写

上面使用的完整值可以使用 grid-column 压缩到列的一行,并使用 grid-row 压缩到行的一行。以下示例将提供与之前代码相同的定位,但 CSS 代码量更少。正斜杠字符 (/) 之前的值为起始线,之后的为结束线。

如果区域只跨越一个轨道,则可以省略结束值。

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

.box1 {
  grid-column: 1 / 4;
  grid-row: 1 / 3;
}

.box2 {
  grid-column: 1;
  grid-row: 3 / 5;
}

网格单元格

网格单元格是网格上最小的单元。从概念上讲,它类似于表格单元格。正如我们在之前的示例中看到的,一旦网格被定义为父元素,子项目将分别在一个定义的网格单元格中进行布局。在下图中,我突出显示了网格的第一个单元格。

The first cell of the grid highlighted

网格区域

项目可以通过行或列跨越一个或多个单元格,这将创建一个网格区域。网格区域必须是矩形的——例如,无法创建 L 形区域。突出显示的网格区域跨越了两行和两列轨道。

A grid area

间隙

可以使用 column-gaprow-gap 属性或简写 gap 创建网格单元格之间的间隙巷道。在下面的示例中,我在列之间创建了 10 像素的间隙,在行之间创建了 1em 的间隙。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 10px;
  row-gap: 1em;
}

注意:当网格首次在浏览器中发布时,column-gaprow-gapgap 前缀为 grid-,分别为 grid-column-gapgrid-row-gapgrid-gap

所有浏览器现在都支持无前缀的值,但是前缀版本将作为别名保留,因此可以安全地使用。

html
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>

间隙使用的任何空间将在将空间分配给灵活长度 fr 轨道之前进行计算,并且间隙在大小调整方面就像常规网格轨道一样,但是您不能将任何内容放置到间隙中。在基于线条的定位方面,间隙就像一条粗线。

嵌套网格

网格项目可以成为网格容器。在以下示例中,我创建了之前创建的三列网格,其中包含我们放置的两个项目。在本例中,第一个项目有一些子项目。由于这些项目不是网格的直接子元素,因此它们不参与网格布局,因此以正常的文档流显示。

Nested grid in flow

不使用子网格嵌套

如果我将 box1 设置为 display: grid,我可以为它提供轨道定义,它也将成为一个网格。然后,项目在此新网格上进行布局。

css
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
html
<div class="wrapper">
  <div class="box box1">
    <div class="nested">a</div>
    <div class="nested">b</div>
    <div class="nested">c</div>
  </div>
  <div class="box box2">Two</div>
  <div class="box box3">Three</div>
  <div class="box box4">Four</div>
  <div class="box box5">Five</div>
</div>
css
* {
  box-sizing: border-box;
}

.wrapper {
  border: 2px solid #f76707;
  border-radius: 5px;
  gap: 3px;
  background-color: #fff4e6;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.box {
  border: 2px solid #ffa94d;
  border-radius: 5px;
  background-color: #ffd8a8;
  padding: 1em;
  color: #d9480f;
}

.box1 {
  grid-column: 1 / 4;
}

.nested {
  border: 2px solid #ffec99;
  border-radius: 5px;
  background-color: #fff9db;
  padding: 1em;
}

在这种情况下,嵌套网格与父网格没有关系。如示例所示,它没有继承父网格的 gap,并且嵌套网格中的线条与父网格中的线条不对齐。

子网格

除了常规网格之外,子网格允许我们创建使用父网格的轨道定义的嵌套网格。

要使用它们,我们将编辑上面的嵌套网格示例,将 grid-template-columns: repeat(3, 1fr) 的轨道定义更改为 grid-template-columns: subgrid。然后,嵌套网格使用父网格轨道来布局项目。

css
.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  display: grid;
  grid-template-columns: subgrid;
}

使用 z-index 分层项目

网格项目可以占据相同的单元格,在这种情况下,我们可以使用 z-index 属性来控制重叠项目堆叠的顺序。

不使用 z-index 重叠

如果我们回到使用行号定位项目的示例,我们可以更改它以使两个项目重叠。

html
<div class="wrapper">
  <div class="box box1">One</div>
  <div class="box box2">Two</div>
  <div class="box box3">Three</div>
  <div class="box box4">Four</div>
  <div class="box box5">Five</div>
</div>
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
}

.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
}

.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
}

项目 box2 现在与 box1 重叠,它显示在顶部,因为它在源代码顺序中排在后面。

控制顺序

我们可以使用 z-index 属性来控制项目堆叠的顺序,就像定位的项目一样。如果我们为 box2 指定比 box1 更低的 z-index,它将显示在 box1 下方。

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

.box1 {
  grid-column-start: 1;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 3;
  z-index: 2;
}

.box2 {
  grid-column-start: 1;
  grid-row-start: 2;
  grid-row-end: 4;
  z-index: 1;
}

后续步骤

在本文中,我们快速了解了网格布局的可能性。探索和玩弄代码示例,然后继续 本指南的下一部分,在那里我们将真正开始深入探讨 CSS 网格布局的细节。