网格布局中的自动放置
CSS 网格布局包含了一些规则,用于控制当你创建一个网格但没有明确地将部分或全部子项放置到网格中时会发生什么。当你不需要对内容放置进行显式控制时,这种“自动放置”是为一组项目创建网格的最简单方法。
默认放置
如果你没有为项目提供放置信息,它们会自动在网格上定位,将每个网格项目放置在一个网格单元中。
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
自动放置的默认规则
正如你在上面的示例中看到的,如果你创建一个网格而没有放置任何项目,子项目将自行布局,每个网格单元中放置一个网格项目,并按源代码顺序排列。默认的流是按行排列项目。网格会将项目放置到第一行的每个单元格中。如果你使用 grid-template-rows 属性创建了额外的行,那么网格将继续将项目放置在这些行中。如果网格的显式网格中没有足够的行来放置所有项目,则会创建新的隐式行。
隐式网格中行的大小调整
隐式网格中自动创建的行的默认设置为自动调整大小。这意味着它们将根据添加的内容自行调整大小,而不会导致溢出。
这些行的大小可以通过 grid-auto-rows 属性控制。例如,要使所有行高为 100px,你可以使用 grid-auto-rows: 100px;
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: 100px;
}
使用 minmax() 调整行的大小
minmax() 函数可以创建具有最小尺寸的行,并且在设置为 grid-auto-rows 值时,可以根据需要增长以适应内容。通过设置 grid-auto-rows: minmax(100px, auto);,我们将每行设置为至少 100px 高,同时允许每行根据需要尽可能高。
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>
    Four <br />This cell <br />Has extra <br />content. <br />Max is auto
    <br />so the row expands.
  </div>
  <div>Five</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: minmax(100px, auto);
}
使用轨道列表调整行的大小
你也可以传入一个轨道列表。这将重复。下面的轨道列表将创建一个初始隐式行轨道为 100 像素,第二个为 200px。只要内容被添加到隐式网格中,这就会继续下去。
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
  <div>Eight</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  grid-auto-rows: 100px 200px;
}
按列自动放置
你还可以要求网格按列自动放置项目。使用 grid-auto-flow 属性,并将其值设置为 column。在这种情况下,网格将把你使用 grid-template-rows 定义的行中添加项目。当它填满一列时,它会移动到下一个显式列,或者在隐式网格中创建一个新的列轨道。与隐式行轨道一样,这些列轨道也将自动调整大小。你可以使用 grid-auto-columns 控制隐式列轨道的大小。这与 grid-auto-rows 的工作方式相同。
在这个例子中,我们有一个网格,其中包含三条 200px 高的行轨道。我们声明 grid-auto-flow: column; 以便按列自动放置。通过 grid-auto-columns: 300px 100px;,创建的列将交替为 300px 宽和 100px 宽,直到有足够的列轨道容纳所有项目。
.wrapper {
  display: grid;
  grid-template-rows: repeat(3, 200px);
  gap: 10px;
  grid-auto-flow: column;
  grid-auto-columns: 300px 100px;
}
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
  <div>Eight</div>
</div>
自动放置项目的顺序
一个网格可以包含混合的项目放置方式。一些项目可能在网格上具有明确定义的位置,而另一些项目可能是自动放置的。如果你的文档顺序反映了项目在网格上的顺序,你可能不需要编写 CSS 规则来放置所有内容。该规范包含一个详细说明网格项目放置算法的长篇章节;然而,对我们大多数人来说,我们只需要记住一些关于我们项目的规则。
修改了文档顺序的顺序
网格将未给定网格位置的项目按照规范中描述的“顺序修改文档顺序”放置。这意味着,如果你使用了 order 属性,项目将按照该顺序放置,而不是它们的 DOM 顺序。否则,它们默认将按照它们在文档源中输入的顺序排列。
具有放置属性的项目
网格首先会放置具有位置的任何项目。在下面的示例中,我有 12 个网格项目。项目 2 和项目 5 已使用基于行的放置方式放置在网格上。你可以看到这些项目是如何放置的,然后其他项目会自动放置在空白处。自动放置的项目会按照 DOM 顺序放置在已放置项目之前,它们不会在位于它们之前的已放置项目的位置之后开始。
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
  <div>Eight</div>
  <div>Nine</div>
  <div>Ten</div>
  <div>Eleven</div>
  <div>Twelve</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 100px;
  gap: 10px;
}
.wrapper div:nth-child(2) {
  grid-column: 3;
  grid-row: 2 / 4;
}
.wrapper div:nth-child(5) {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}
处理跨轨道的项目
你可以在利用自动放置的同时使用放置属性。在下一个示例中,我通过将项目 1、5 和 9 (4n+1) 设置为跨越两行和两列轨道来增强布局。我通过 grid-column-end 和 grid-row-end 属性并将其值设置为 span 2 来实现。这意味着项目的起始线将由自动放置设置,而结束线将跨越两个轨道。
你可以看到这如何在网格中留下空白,因为对于自动放置的项目,如果网格遇到一个不适合轨道的项目,它将移动到下一行,直到找到一个项目可以容纳的空间。
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
  <div>Eight</div>
  <div>Nine</div>
  <div>Ten</div>
  <div>Eleven</div>
  <div>Twelve</div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 100px;
  gap: 10px;
}
.wrapper div:nth-child(4n + 1) {
  grid-column-end: span 2;
  grid-row-end: span 2;
  background-color: #ffa94d;
}
.wrapper div:nth-child(2) {
  grid-column: 3;
  grid-row: 2 / 4;
}
.wrapper div:nth-child(5) {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}
填补空隙
到目前为止,除了我们明确放置的项目外,网格始终向前推进并保持项目在 DOM 顺序中。这通常是你想要的,例如,如果你正在布局一个表单,你不会希望标签和字段为了填补一些空白而混淆。然而,有时,我们布局的东西没有逻辑顺序,我们希望创建一个没有空白的布局。
要做到这一点,在容器上添加属性 grid-auto-flow,并将其值设置为 dense。这与你用来将流顺序更改为 column 的属性相同,因此如果你在列中工作,你将同时添加两个值 grid-auto-flow: column dense。
完成此操作后,网格现在将回填空白,因为它在网格中移动时像以前一样留下空白,但如果它发现一个适合先前空白的项目,它将拿起该项目并将其从 DOM 顺序中取出以放置在空白中。与网格中的任何其他重新排序一样,这不会改变逻辑顺序。例如,Tab 键顺序仍将遵循文档顺序。我们将在网格布局和可访问性指南中探讨网格布局的潜在可访问性问题,但你在创建这种视觉顺序和显示顺序之间的不一致时应谨慎。
<div class="wrapper">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
  <div>Eight</div>
  <div>Nine</div>
  <div>Ten</div>
  <div>Eleven</div>
  <div>Twelve</div>
</div>
.wrapper div:nth-child(4n + 1) {
  grid-column-end: span 2;
  grid-row-end: span 2;
  background-color: #ffa94d;
}
.wrapper div:nth-child(2) {
  grid-column: 3;
  grid-row: 2 / 4;
}
.wrapper div:nth-child(5) {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}
.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 100px;
  gap: 10px;
  grid-auto-flow: dense;
}
匿名网格项目
规范中提到了匿名网格项。如果你在网格容器内有一段未被任何其他元素包裹的文本字符串,就会创建这些项。在下面的示例中,我们有三个网格项,假设你已将父级类为 grid 的元素设置为 display: grid。第一个是匿名项,因为它没有封闭的标记,这个项将始终通过自动放置规则处理。另外两个是包裹在 div 中的网格项,它们可以自动放置,或者你可以使用定位方法将它们放置到网格上。
<div class="grid">
  I am a string and will become an anonymous item
  <div>A grid item</div>
  <div>A grid item</div>
</div>
匿名项总是自动放置的,因为无法定位它们。因此,如果你的网格中出于某种原因有未包装的文本,请注意它可能会出现在意想不到的地方,因为它将根据自动放置规则进行自动放置。
自动放置的用例
当你有一组项目时,自动放置非常有用。这可能是不具有逻辑顺序的项目,例如照片画廊或产品列表。在这种情况下,你可能会选择使用紧密打包模式来填补网格中的任何空洞。在我的图片画廊示例中,我有一些横向和一些纵向图片。我将横向图片(带有 landscape 类)设置为跨越两个列轨道。然后我使用 grid-auto-flow: dense 来创建一个紧密打包的网格。
尝试删除 grid-auto-flow: dense 这行,查看内容如何重排以在布局中留下空白。
<ul class="wrapper">
  <li>
    <img
      alt="A colorful hot air balloon against a clear sky"
      src="https://mdn.github.io/shared-assets/images/examples/balloon.jpg" />
  </li>
  <li class="landscape">
    <img
      alt="Three hot air balloons against a clear sky, as seen from the ground"
      src="https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg" />
  </li>
  <li class="landscape">
    <img
      alt="Three hot air balloons against a clear sky, as seen from the ground"
      src="https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg" />
  </li>
  <li class="landscape">
    <img
      alt="Three hot air balloons against a clear sky, as seen from the ground"
      src="https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg" />
  </li>
  <li>
    <img
      alt="A colorful hot air balloon against a clear sky"
      src="https://mdn.github.io/shared-assets/images/examples/balloon.jpg" />
  </li>
  <li>
    <img
      alt="A colorful hot air balloon against a clear sky"
      src="https://mdn.github.io/shared-assets/images/examples/balloon.jpg" />
  </li>
</ul>
.wrapper {
  display: grid;
  grid-template-columns: repeat(3, minmax(120px, 1fr));
  gap: 10px;
  grid-auto-flow: dense;
}
.wrapper li.landscape {
  grid-column-end: span 2;
}
自动放置还可以帮助你布局具有逻辑顺序的界面项目。一个例子是下一个示例中的定义列表。定义列表是一种有趣的样式挑战,因为它们是扁平的,没有任何东西包裹着 dt 和 dd 项组。在我的示例中,我允许自动放置项目,但是我有类,它们将 dt 放在第 1 列,dd 放在第 2 列,这确保了术语在一侧,定义在另一侧——无论每种有多少。
<div class="wrapper">
  <dl>
    <dt>Mammals</dt>
    <dd>Cat</dd>
    <dd>Dog</dd>
    <dd>Mouse</dd>
    <dt>Birds</dt>
    <dd>Pied Wagtail</dd>
    <dd>Owl</dd>
    <dt>Fish</dt>
    <dd>Guppy</dd>
  </dl>
</div>
dl {
  display: grid;
  grid-template-columns: auto 1fr;
  max-width: 300px;
  margin: 1em;
  line-height: 1.4;
}
dt {
  grid-column: 1;
  font-weight: bold;
}
dd {
  grid-column: 2;
}
自动放置目前(还)不能做什么?
有一些问题经常被提及。目前我们还不能做到像用我们的项目定位网格的每个其他单元格。如果你遵循了关于网格上命名行的上一个指南,一个相关的问题可能已经浮现在脑海中。那就是定义一个规则,说明“将项目自动放置到下一个名为‘n’的行上”,然后网格将跳过其他行。关于这个问题在 CSSWG GitHub 存储库上已经有人提出了,欢迎你添加你自己的用例。
你可能会为自动放置或网格布局的任何其他部分提出自己的用例。如果你这样做了,请将它们作为问题提出,或添加到现有问题中,这可能解决你的用例。这将有助于使未来的规范版本更好。