网格模板区域

基于线的网格布局指南中,我们了解了网格线以及如何根据这些线来定位项目。当你使用 CSS 网格布局时,你总会用到线,这是一种在网格上放置项目的直接方法。然而,还有另一种方法可用于在网格上定位项目,你可以单独使用它,也可以与基于线的定位结合使用。这种方法是使用命名的模板区域来放置项目。你很快就会明白为什么我们有时称之为网格布局的“ASCII 艺术”方法!

命名网格区域

你已经遇到过 grid-area 属性了。这个属性的值可以包含用于定位一个网格区域的所有四条线。

css
.box1 {
  grid-area: 1 / 1 / 4 / 2;
}

当我们定义所有四条线时,我们实际上是通过指定包围该区域的线来定义这个区域。

The grid area defined by lines

我们也可以通过给一个区域命名,然后在 grid-template-areas 属性的值中指定该区域的位置来定义一个区域。你可以随意选择你想要的区域名称。例如,如果我们希望创建下面所示的布局,我们可以确定四个主要区域。

  • 页眉(header)
  • 页脚(footer)
  • 侧边栏(sidebar)
  • 主内容区(main content)

An image showing a two column layout with header and footer

通过 grid-area 属性,我们可以给这些区域分别命名。这本身并不会创建任何布局。相反,它只是提供了可在布局中使用的命名区域。

css
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}

定义了这些名称后,我们就可以创建布局了。这次,我们不是在项目本身上使用行号来放置项目,而是在网格容器上创建整个布局。这里我们创建了一个 9 列的网格,并指定 hdft 区域跨越所有 9 列,而 sd 跨越三列,main 跨越六列。每个区域只跨越一行。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    "ft ft ft ft   ft   ft   ft   ft   ft";
}
html
<div class="wrapper">
  <div class="header">Header</div>
  <div class="sidebar">Sidebar</div>
  <div class="content">Content</div>
  <div class="footer">Footer</div>
</div>

使用这种方法,我们完全不需要在单个网格项上指定任何东西,一切都在我们的网格容器上完成。我们可以看到布局被描述为 grid-template-areas 属性的值。

留空网格单元格

在这个例子中,我们用区域完全填满了网格,没有留下任何空白。但是,你也可以用这种布局方法将网格单元格留空。要留空一个单元格,请使用句点字符 .。如果我们只想让页脚直接显示在主内容下方,我们就需要将侧边栏下方的三个单元格留空。

css
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    ".  .  .  ft   ft   ft   ft   ft   ft";
}
html
<div class="wrapper">
  <div class="header">Header</div>
  <div class="sidebar">Sidebar</div>
  <div class="content">Content</div>
  <div class="footer">Footer</div>
</div>

为了使布局更整洁,我们可以使用多个 . 字符。只要句点之间至少有一个空格,它就会被算作一个单元格。对于复杂的布局,让行和列整齐对齐是有好处的。这意味着你可以直接在 CSS 中看到这个布局的样子。

跨越多个单元格

在我们的例子中,每个区域都跨越了多个网格单元格,我们通过用空格分隔并多次重复该网格区域的名称来实现这一点。你可以在 grid-template-areas 的值中添加额外的空格来保持列的整齐对齐。你可以看到我们已经这样做了,使得 hdft 区域与 main 对齐。

通过链接区域名称创建的区域必须是矩形的,目前还没有办法创建一个 L 形的区域。规范确实提到未来的版本可能会提供这个功能。然而,你可以像跨越列一样轻松地跨越行。例如,我们可以通过将 . 替换为 sd,使我们的侧边栏向下延伸到页脚的末尾。

css
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}
css
.wrapper {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-template-areas:
    "hd hd hd hd   hd   hd   hd   hd   hd"
    "sd sd sd main main main main main main"
    "sd sd sd  ft  ft   ft   ft   ft   ft";
}

grid-template-areas 的值必须显示一个完整的网格,否则它是无效的(并且该属性会被忽略)。这意味着你每一行必须有相同数量的单元格,如果是空的,则用一个句点字符来表示该单元格将被留空。如果你的区域不是矩形的,你也会创建一个无效的网格。

使用媒体查询重新定义网格

由于我们的布局现在包含在 CSS 的一个部分中,这使得在不同的断点处进行更改变得非常容易。你可以通过重新定义网格、网格上项目的位置,或者两者同时进行来实现这一点。

在这样做时,请在任何媒体查询之外定义你的区域名称。这样,无论内容区域被放置在网格的哪个位置,它都将始终被称为 main

对于我们上面的布局,我们可能希望在窄宽度下有一个非常基本的布局,定义一个单列网格,并将我们的四个项目堆叠成四行。

css
.header {
  grid-area: hd;
}
.footer {
  grid-area: ft;
}
.content {
  grid-area: main;
}
.sidebar {
  grid-area: sd;
}

.wrapper {
  display: grid;
  grid-auto-rows: minmax(100px, auto);
  grid-template-columns: 1fr;
  grid-template-areas:
    "hd"
    "main"
    "sd"
    "ft";
}

然后我们可以在 媒体查询 中重新定义该布局,以变为我们的两列布局,如果可用空间更宽,甚至可以将其变为三列布局。请注意,对于宽布局,我们保留了九列的轨道网格,仅使用 grid-template-areas 重新定义了项目的位置。

css
@media (width >= 30em) {
  .wrapper {
    grid-template-columns: repeat(9, 1fr);
    grid-template-areas:
      "hd hd hd hd   hd   hd   hd   hd   hd"
      "sd sd sd main main main main main main"
      "sd sd sd  ft  ft   ft   ft   ft   ft";
  }
}
@media (width >= 60em) {
  .wrapper {
    grid-template-areas:
      "hd hd hd   hd   hd   hd   hd   hd hd"
      "sd sd main main main main main ft ft";
  }
}

为 UI 元素使用 grid-template-areas

你会在网上找到的许多网格示例都假定你会将网格用于主页面布局,然而,网格对于小元素和大元素同样有用。使用 grid-template-areas 可能特别好,因为在代码中很容易看到你的元素长什么样。

媒体对象示例

举个例子,我们可以创建一个“媒体对象”。这是一个组件,一侧有空间放置图像或其他媒体,另一侧是内容。图像可以显示在盒子的右侧或左侧。

Images showing an example media object design

我们的网格是一个两列轨道网格,图像的列大小为 1fr,文本为 3fr。如果你想要一个固定宽度的图像区域,那么你可以将图像列设置为像素宽度,并为文本区域分配 1fr1fr 的单个列轨道将占据剩余的空间。

我们给图像区域一个名为 img 的网格区域名称,给文本区域一个名为 content 的名称,然后我们可以使用 grid-template-areas 属性来布局它们。

css
* {
  box-sizing: border-box;
}

.media {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  max-width: 400px;
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-template-areas: "img content";
  margin-bottom: 1em;
}

.media .image {
  grid-area: img;
  background-color: #ffd8a8;
}

.media .text {
  grid-area: content;
  padding: 10px;
}
html
<div class="media">
  <div class="image"></div>
  <div class="text">
    This is a media object example. We can use grid-template-areas to switch
    around the image and text part of the media object.
  </div>
</div>

将图像显示在盒子的另一侧

我们可能希望能够将我们的盒子以相反的方式显示图像。为此,我们重新定义网格,将 1fr 轨道放在最后,并翻转 grid-template-areas 中的值。

css
* {
  box-sizing: border-box;
}

.media {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  max-width: 400px;
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-template-areas: "img content";
  margin-bottom: 1em;
}

.media.flipped {
  grid-template-columns: 3fr 1fr;
  grid-template-areas: "content img";
}

.media .image {
  grid-area: img;
  background-color: #ffd8a8;
}

.media .text {
  grid-area: content;
  padding: 10px;
}
html
<div class="media flipped">
  <div class="image"></div>
  <div class="text">
    This is a media object example. We can use grid-template-areas to switch
    around the image and text part of the media object.
  </div>
</div>

网格定义简写

在看过了在网格上放置项目的各种方法以及许多用于定义网格的属性之后,现在是时候看一看几个可用于在一行 CSS 中定义网格及其许多相关内容的简写属性了。

这些简写属性可能很快就会变得让其他开发者,甚至未来的你难以阅读。然而,它们是规范的一部分,你很可能会在示例中或在其他开发者的使用中遇到它们,即使你选择不使用它们。

在使用任何简写属性之前,值得记住的是,简写不仅能让你一次性设置多个属性,它们还会将你没有(或不能)在简写中设置的所有内容**重置**为其初始值。因此,如果你使用简写,请注意它可能会重置你在别处应用的设置。

网格容器的两个简写属性是显式网格简写 grid-template 和网格定义简写 grid

grid-template

grid-template 简写属性设置了以下完整属性:

该属性被称为*显式网格简写*,因为它设置的是你在定义显式网格时控制的值,而不是那些影响任何可能创建的隐式行或列轨道的值。

以下代码使用 grid-template 创建了一个布局,该布局与本指南前面创建的布局相同。

css
.wrapper {
  display: grid;
  grid-template:
    "hd hd hd hd   hd   hd   hd   hd   hd" minmax(100px, auto)
    "sd sd sd main main main main main main" minmax(100px, auto)
    "ft ft ft ft   ft   ft   ft   ft   ft" minmax(100px, auto)
    / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

第一个值是我们的 grid-template-areas 值,但我们还在每行的末尾声明了行的大小。这就是 minmax(100px, auto) 的作用。

然后在 grid-template-areas 之后有一个正斜杠,之后是列轨道的显式轨道列表。

grid

grid 简写更进一步,还设置了隐式网格使用的属性。所以你将设置:

你可以以与 grid-template 简写完全相同的方式使用此语法。只需注意,这样做会重置该属性设置的其他值。

css
.wrapper {
  display: grid;
  grid:
    "hd hd hd hd   hd   hd   hd   hd   hd" minmax(100px, auto)
    "sd sd sd main main main main main main" minmax(100px, auto)
    "ft ft ft ft   ft   ft   ft   ft   ft" minmax(100px, auto)
    / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

当我们学习网格布局中的自动放置grid-auto-flow 属性时,我们将重新审视此简写提供的其他功能。

后续步骤

如果你一直在学习网格指南,你应该能够使用基于线的放置命名区域来创建网格布局。现在让我们来看看如何使用命名网格线创建网格布局