使用命名网格线进行布局

在之前的指南中,我们已经学习了如何将项目放置在通过定义网格轨道创建的网格线上,以及如何使用命名模板区域来放置项目。在本指南中,我们将探讨当我们使用命名网格线时,这两种方法是如何协同工作的。

网格线命名非常有用,但一些更令人困惑的网格语法恰恰来自于名称和轨道尺寸的这种组合。一旦你完成一些示例,它应该会变得更清晰、更容易使用。

定义网格时命名网格线

当你使用 grid-template-rowsgrid-template-columns 属性定义网格时,可以为网格中的部分或全部网格线指定一个名称。为了演示,我们将使用在基于网格线的布局指南中创建的基本布局。这一次,我们将使用命名网格线来创建网格。

在定义网格时,我们在方括号([])内为网格线命名。这些名称可以是任何你喜欢的名字。我们为容器的起始和结束线都定义了名称,包括行和列。在这个例子中,网格中心块的起始行和列都命名为 content-start,其结束行和列都命名为 content-end

css
.wrapper {
  display: grid;
  grid-template-columns: [main-start] 1fr [content-start] 1fr [content-end] 1fr [main-end];
  grid-template-rows: [main-start] 100px [content-start] 100px [content-end] 100px [main-end];
}

我们不必为网格中的所有网格线命名;你可以选择只命名布局中的关键网格线。

一旦网格线有了名称,我们就可以使用我们定义的名称,而不是行号,来放置网格项目。

css
.box1 {
  grid-column-start: main-start;
  grid-row-start: main-start;
  grid-row-end: main-end;
}

.box2 {
  grid-column-start: content-end;
  grid-row-start: main-start;
  grid-row-end: content-end;
}

.box3 {
  grid-column-start: content-start;
  grid-row-start: main-start;
}

.box4 {
  grid-column-start: content-start;
  grid-column-end: main-end;
  grid-row-start: content-end;
}
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>

基于网格线布局的其他所有功能仍然以相同的方式工作。在我们的网格布局中,我们为每个数字网格线提供了一个别名。在我们的网格项目中,我们引用的是名称而不是数字。以这种方式命名网格线非常有用——在创建响应式设计时,我们可以更新容器的网格属性,而无需在每个媒体查询中更新网格项目。

为网格线指定多个名称

你可能想给一条网格线多个名称,例如,它可能同时表示侧边栏的结束(sidebar-end)和主内容的开始(main-start)。要做到这一点,请在方括号内添加名称,并用空格隔开,如 [sidebar-end main-start]。然后你就可以通过任何一个名称来引用该网格线。

通过命名网格线创建隐式网格区域

在命名网格线时,我们提到你可以随意命名。这个名称是一个 <custom-ident>,即一个由作者定义的名称。在选择名称时,你需要避免使用可能出现在规范中并引起混淆的词语——比如 span。标识符不需要加引号。

虽然你可以选择任何名称,但如果你像我们上面的例子那样,在区域周围的网格线上附加 -start-end,网格将会创建一个以主名称命名的区域。以上面的例子来说,我们有 content-startcontent-end,同时用于行和列。这意味着我们得到了一个名为 content 的网格区域,如果需要,我们可以将内容放置在该区域中。

我们使用与上面相同的网格定义,将一个项目放置到名为 content 的命名区域中。

css
.wrapper {
  display: grid;
  grid-template-columns: [main-start] 1fr [content-start] 1fr [content-end] 1fr [main-end];
  grid-template-rows: [main-start] 100px [content-start] 100px [content-end] 100px [main-end];
}
.thing {
  grid-area: content;
}
html
<div class="wrapper">
  <div class="thing">I am placed in an area named content.</div>
</div>

我们不需要用 grid-template-areas 来定义我们的区域,因为我们的命名网格线已经为我们创建了一个区域。

通过命名区域创建隐式网格线

我们已经看到命名网格线如何创建一个命名区域,反之亦然。命名模板区域会创建可用于放置项目的命名网格线。如果我们采用网格模板区域指南中创建的布局,我们可以使用由区域创建的网格线来了解其工作原理。

在这个例子中,我们添加了一个额外的 <div>,其 class 为 overlay。我们使用 grid-area 属性创建了命名区域,然后在 grid-template-areas 中创建了布局。区域名称是

  • hd
  • ft
  • main
  • sd

这为我们提供了以下列和行网格线

  • hd-start
  • hd-end
  • sd-start
  • sd-end
  • main-start
  • main-end
  • ft-start
  • ft-end

你可以在图中看到这些命名的网格线。注意,有些网格线有两个名称——例如,sd-endmain-start 指的是同一条列网格线。

An image showing the implicit line names created by our grid areas.

使用这些隐式命名的网格线来定位 overlay 与使用显式命名的网格线来定位项目是一样的。

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

.header {
  grid-area: hd;
}

.footer {
  grid-area: ft;
}

.content {
  grid-area: main;
}

.sidebar {
  grid-area: sd;
}

.wrapper > div.overlay {
  z-index: 10;
  grid-column: main-start / main-end;
  grid-row: hd-start / ft-end;
  border: 4px solid rgb(92 148 13);
  background-color: rgb(92 148 13 / 40%);
  color: rgb(92 148 13);
  font-size: 150%;
}
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 class="overlay">Overlay</div>
</div>

鉴于我们有能力通过命名区域创建的网格线进行定位,以及通过命名网格线创建区域,因此在开始创建网格布局时,花点时间规划你的命名策略是值得的。选择对你和你的团队有意义的名称,将使你的布局更加直观。

使用 repeat() 创建多条同名网格线

如果你想给所有网格线一个唯一的名称,你需要使用长格式属性来定义轨道,而不是使用 repeat 语法,因为名称需要在定义轨道时添加到方括号中。如果你确实使用了 repeat 语法,你会得到多条同名的网格线,这可能有用,也可能令人困惑,具体取决于你的布局要求。

使用 repeat() 创建十二列网格

在这个例子中,我们创建了一个包含 12 个等宽列的网格。在定义列轨道的 1fr 大小之前,我们定义了一条名为 [col-start] 的网格线。这意味着我们将拥有一个网格,其中有 12 条都名为 col-start 的列网格线,每条线后面都跟着一个 1fr 宽度的列。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
}

创建网格后,你可以在上面放置项目。由于我们有多条名为 col-start 的网格线,如果你将一个项目放置在 col-start 网格线之后开始,那么将使用第一条名为 col-start 的网格线。在我们的例子中,这是最左边的线。要指定另一条线,请使用名称加上该线的序号。

要将一个项目放置在从第一条名为 col-start 的网格线跨越到第五条同名网格线的位置,我们可以使用

css
.item1to5 {
  grid-column: col-start / col-start 5;
}

你也可以使用 span 关键字。这个项目将从第七条名为 col-start 的网格线开始,跨越 3 条网格线。

css
.item7to9 {
  grid-column: col-start 7 / span 3;
}
html
<div class="wrapper">
  <div class="item1to5">I am placed from col-start line 1 to col-start 5</div>
  <div class="item7to9">I am placed from col-start line 7 spanning 3 lines</div>
</div>

如果你在浏览器的开发者工具中查看这个布局,你会看到列网格线是如何显示的,以及我们的项目是如何根据这些网格线放置的。

The 12 column grid with items placed. The Firefox grid highlighter shows the position of the lines.

使用轨道列表定义命名网格线

repeat() 语法也可以接受一个轨道列表;它不仅仅可以重复单个轨道尺寸。

这段 CSS 创建了一个八轨道的网格,其中一个较窄的 1fr 宽度的列,其起始线名为 col1-start,后面跟着一个较宽的 3fr 宽度的列,其起始线名为 col2-start

css
.wrapper {
  grid-template-columns: repeat(4, [col1-start] 1fr [col2-start] 3fr);
}

如果你的重复语法将两条线放在一起,那么它们将被合并,并产生与在非重复轨道定义中为一条线指定多个名称相同的结果。以下定义创建了四个 1fr 的轨道,每个轨道都有一个起始线和一个结束线。

css
.wrapper {
  grid-template-columns: repeat(4, [col-start] 1fr [col-end]);
}

如果我们不使用 repeat 写法,这个声明看起来是这样的

css
.wrapper {
  grid-template-columns: [col-start] 1fr [col-end col-start] 1fr [col-end col-start] 1fr [col-end col-start] 1fr [col-end];
}

使用轨道列表,我们可以使用 span 关键字来跨越一定数量的网格线,包括特定名称的网格线。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(6, [col1-start] 1fr [col2-start] 3fr);
}

.item1 {
  grid-column: col1-start / col2-start 2;
}

.item2 {
  grid-row: 2;
  grid-column: col1-start 2 / span 2 col1-start;
}
html
<div class="wrapper">
  <div class="item1">
    I am placed from col1-start line 1 to col2-start line 2
  </div>
  <div class="item2">
    I am placed from col1-start line 2 spanning 2 lines named col1-start
  </div>
</div>

十二列网格框架

在学习了基于数字和命名的网格线布局以及网格模板区域之后,我们现在知道有多种方法可以使用 CSS 网格布局来放置项目。这可能看起来过于复杂,但你不需要全部使用它们。在实践中,使用命名模板区域对于简单的布局效果很好,因为这种方法能很好地在视觉上呈现你的布局是什么样子,并使在网格上移动内容变得更加直观。例如,在处理严格的多列布局时,本指南最后一部分演示的命名网格线就很好用。

像 Foundation 或 Bootstrap 这样的传统网格系统是基于 12 列网格的。这些框架需要引入代码来进行计算,以确保列的总和达到 100%。但其实并不需要框架!我们只需要以下 CSS 就可以创建一个 12 列网格“框架”:

css
.wrapper {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(12, [col-start] 1fr);
}

然后我们可以使用这个“框架”来布局我们的页面。

例如,要创建一个带有页眉和页脚的三列布局,我们可以使用以下标记。

html
<div class="wrapper">
  <header class="main-header">I am the header</header>
  <aside class="side1">I am sidebar 1</aside>
  <article class="content">I am the main article</article>
  <aside class="side2">I am sidebar 2</aside>
  <footer class="main-footer">I am the footer</footer>
</div>

我们可以将其放置在我们的网格布局框架上

css
.main-header,
.main-footer {
  grid-column: col-start / span 12;
}

.side1 {
  grid-column: col-start / span 3;
  grid-row: 2;
}

.content {
  grid-column: col-start 4 / span 6;
  grid-row: 2;
}

.side2 {
  grid-column: col-start 10 / span 3;
  grid-row: 2;
}

再次强调,开发者工具的网格高亮器有助于向我们展示我们放置项目的网格是如何工作的。

The layout with the grid highlighted.

这就是我们所需要的全部。我们不需要做任何计算!CSS 网格布局在为 1fr 列轨道分配空间之前,自动减去了我们 10 像素的间距轨道。

接下来,我们将探讨 CSS 网格布局如何在完全不需要放置属性的情况下为我们定位项目,详见网格布局中的自动放置指南。