网格布局的基本概念

CSS 网格布局为 CSS 引入了一个二维网格系统。网格可以用来布局主要的页面区域或小的用户界面元素。本指南介绍了 CSS 网格布局以及 CSS 网格布局规范中的术语。本概述中展示的功能将在本系列的其他指南中进行更详细的解释。

什么是网格?

网格是一组定义行和列的相交的水平线和垂直线。元素可以被放置在这些行和列线组成的网格上。CSS 网格布局具有以下特点:

固定和灵活的轨道尺寸

你可以创建具有固定轨道尺寸的网格——例如使用像素。这将网格设置为指定的像素,以适应你期望的布局。你还可以使用百分比或为此目的设计的 fr 单位创建具有灵活尺寸的网格。

项目放置

你可以使用行号、名称或通过指定网格的一个区域将项目放置在网格上的精确位置。网格还包含一种算法,用于控制未在网格上明确指定位置的项目的放置。

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

你可以使用网格布局定义一个显式网格。网格布局模块定义的功能提供了在需要时添加额外行和列的灵活性。其中包含了诸如“添加尽可能多的列以适应容器”等功能。

对齐控制

CSS 网格布局和 CSS 盒模型对齐功能使我们能够控制项目在放入网格区域后的对齐方式,以及整个网格的对齐方式。

控制重叠内容

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

网格是一种强大的布局方法,当与 CSS 的其他部分(如 flexbox)结合使用时,可以帮助你创建响应式、灵活且易于访问的布局。这一切都始于在你的网格容器中创建一个网格。

网格容器

我们通过在一个元素上声明 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>

我们使用 display: grid; 使 .wrapper 成为一个网格容器。

css
.wrapper {
  display: grid;
}

所有的直接子元素现在都是网格项目。在 Web 浏览器中,你不会看到这些项目在变成网格前后显示上有任何差异,因为网格为这些项目创建了一个单列网格。如果你在浏览器的开发者工具中检查网格,你可能会在 grid 值旁边看到一个小图标。点击它,在大多数浏览器中,该元素的网格将在浏览器窗口中叠加显示。

Using the grid highlighter in Firefox 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 单位。第一个轨道是 500px,所以这个固定宽度会从可用空间中减去。剩余的空间被分成三份,并按比例分配给两个灵活轨道。

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);
}

Repeat 表示法可以用于轨道列表的一部分。在这个例子中,我们创建了一个 8 列网格;初始轨道是 20px,然后是一个重复 6 个 1fr 轨道的区域,最后是一个 20px 的轨道。

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

Repeat 表示法(repeat())使用轨道列表来创建重复的轨道模式。在这个例子中,网格将有 10 个轨道;一个 1fr 轨道后面跟着一个 2fr 轨道,这个模式重复五次。

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

隐式网格和显式网格

在创建我们的示例网格时,我们使用 grid-template-columns 属性明确定义了我们的列轨道,网格会根据需要创建行来容纳内容。列定义了显式网格,而行是隐式网格的一部分。

显式网格由使用 grid-template-columnsgrid-template-rows 定义的行和列组成。当内容被放置在显式网格之外时(例如,通过绘制额外的网格线放入行中),隐式网格会扩展已定义的显式网格。

如果你将某个项目放置在定义的网格之外,或者由于内容量需要更多的网格轨道,那么网格会在隐式网格中创建行和列。这些隐式轨道默认是自动调整大小的,这意味着创建的行或列的大小受其内容和网格容器内可用自由空间的影响。auto 关键字允许生成的轨道适应内容,同时共享任何剩余空间。

你也可以使用 grid-auto-rowsgrid-auto-columns 属性为在隐式网格中创建的轨道定义一个固定大小。

在这个例子中,我们设置了 grid-auto-rows: 200px,确保在这个隐式网格中创建的轨道高度为 200px

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()。通过设置 grid-auto-rows: minmax(100px, auto);,自动创建的行将至少有 100px 高,最大值为 auto。将 auto 设置为最大值允许轨道增长以适应其内容(直到其 max-content 大小),同时共享网格容器内的任何可用自由空间。

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

网格线

应该注意的是,当我们定义一个网格时,我们定义的是网格轨道,而不是网格线。然后,网格会给我们编号的线来用于定位项目。在我们的三列两行的网格中,我们有四条列线。

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;
}

使用你的开发者工具中的网格检查器,看看项目是如何根据网格线定位的。

线定位的简写

上面使用的长手写值可以用 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;
}
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

不使用 subgrid 的嵌套

如果我们将 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,并且嵌套网格中的线与父网格中的线不对齐。

子网格

除了常规网格,我们还可以创建子网格subgrid 值让我们能够创建使用父网格轨道定义的嵌套网格。

要使用它们,我们编辑上面的嵌套网格例子,将 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 网格布局的细节。