CSS Grid 中自动调整列大小:`auto-fill` 与 `auto-fit`

Avatar of Sara Soueidan
Sara Soueidan

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

CSS Grid 最强大、最方便的功能之一是,除了显式列大小调整外,我们还可以选择在网格中重复填充列,然后自动将项目放置其中。更具体地说,我们可以指定网格中所需的列数,然后让浏览器处理这些列的响应性,在较小的视口尺寸上显示较少的列,并在屏幕空间允许的情况下显示更多列,而无需编写任何媒体查询来控制这种响应行为。

我们只需一行 CSS 就能做到这一点——这行代码让我想起了邓布利多在霍拉斯的公寓里挥舞魔杖时的情景,“家具飞回原位;装饰品在空中重塑,羽毛飞回它们的靠垫;撕破的书在落回书架时自行修复……”

这种神奇的、无需媒体查询的响应性是通过使用 `repeat()` 函数和自动放置关键字实现的。

总而言之,`repeat()` 函数允许您根据需要重复列。例如,如果您正在创建 12 列网格,您可以编写以下单行代码

.grid {
   display: grid;

  /* define the number of grid columns */
  grid-template-columns: repeat(12, 1fr);
}

`1fr` 用于告诉浏览器在列之间分配空间,以便每列都平均获得该空间的一部分。也就是说,它们都是流体、等宽的列。在这个例子中,无论网格有多宽,它始终都会有 12 列。正如您可能猜到的那样,这还不够好,因为内容在较小的视口中会过于压缩。

因此,我们需要首先为列指定最小宽度,确保它们不会变得太窄。我们可以使用 `minmax()` 函数来实现。

grid-template-columns: repeat( 12, minmax(250px, 1fr) );

但是,根据 CSS Grid 的工作原理,这会导致行溢出。如果视口宽度太窄,无法在满足新的最小宽度要求的情况下容纳所有列,则列不会换行到新行,因为我们明确地告诉浏览器每行重复 12 次列。

为了实现换行,我们可以使用 `auto-fit` 或 `auto-fill` 关键字。

grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );

这些关键字告诉浏览器帮我们处理列大小和元素换行,以便当宽度不足以容纳所有列而不会溢出时,元素会换行到新行。我们使用的分数单位也确保,如果宽度允许容纳部分列但不能容纳整列,则该空间将改为分配到已容纳的列或列上,确保行末不会留下任何空白。

乍一看名称,`auto-fill` 和 `auto-fit` 似乎是相反的。但实际上,它们之间的区别非常细微。

也许您觉得使用 `auto-fit` 会在列的末尾获得额外的空间。但何时以及如何发生?

让我们看看幕后到底发生了什么。

填充还是适应?有什么区别?

在最近的一场 CSS 研讨会上,我总结了 `auto-fill` 和 `auto-fit` 之间的区别,如下所示

`auto-fill` 用尽可能多的列填充行。因此,只要可以容纳新列,它就会创建隐式列,因为它试图用尽可能多的列填充行。新添加的列可能是空的,但它们仍将占用行中指定的空间。

`auto-fit` 通过扩展列来适应当前可用的空间,以便它们占据任何可用空间。浏览器在用额外的列(如 `auto-fill`)填充额外空间后,再折叠空列来做到这一点。

这起初可能听起来令人困惑,但当您可视化这种行为时,它就变得更有意义了。因此,我们将确切地这样做,使用 Firefox DevTools 的网格检查器帮助我们可视化网格项目和列的大小和位置。

请考虑以下演示作为一个例子。

列使用 `repeat()` 函数定义,最小宽度为 100px,最大宽度设置为 `1fr`,以便在可用时它们可以扩展并平均共享任何额外空间。至于每行的列数,我们将使用自动放置关键字,以便让浏览器处理网格的响应性,并在需要时将列换行到新行。

浏览器将使用 `auto-fill` 关键字在第一个示例中放置和调整列的大小,并将在第二个示例中使用 `auto-fit`。

.grid-container--fill {
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}

.grid-container--fit {
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
}

在达到某个点之前,`auto-fill` 和 `auto-fit` 显示相同的结果。

但它们在幕后的行为并不相同。碰巧的是,它们在达到某个视口宽度之前会给出相同的结果

这两个关键字开始表现出不同行为的点取决于 `grid-template-columns` 中定义的列数和大小,因此它在不同的示例中会有所不同。

当视口足够宽以容纳一行中的一列(或多列)额外列时,这两个关键字之间的差异变得明显。此时,浏览器有两种处理这种情况的方法,它如何处理在很大程度上取决于是否有内容要放置到该额外列中。

因此,当行可以容纳新列时,浏览器会执行以下操作

  1. “我有一些空间可以容纳新列。我是否有任何内容(即网格项目)要放入该额外列中?有?好的,一切正常。我将把该列添加到行中,它将在较小的视口中换行到新行。”
  2. 在没有内容要放置到新列的情况下:“我是否允许这新列占用行中的空间(从而影响其余行的位置和大小)?或者我是否折叠该列并使用其空间扩展其他列?”

`auto-fill` 和 `auto-fit` 特别地提供了这个问题的答案,并指示浏览器如何处理这种情况。折叠还是不折叠,这是一个问题。这也是答案。
您是否希望它折叠取决于您的内容以及您希望该内容在响应式设计环境中的行为。

让我们看看它是如何工作的。要可视化 `auto-fill` 和 `auto-fit` 之间的差异,请查看以下屏幕录制。我正在调整视口大小,使其足够大以创建足够容纳一行中的一列(或多列)的水平空间。请记住,这两行是相同的,并且具有完全相同的内容和列数。此演示中的唯一区别是我对第一个使用 `auto-fill`,对第二个使用 `auto-fit`。

注意那里发生了什么?如果仍然不清楚,以下录制应该可以使它更清楚

`auto-fill` 行为:“填充该行!添加尽可能多的列。我不在乎它们是否为空——它们都应该显示出来。如果您有足够的空间添加列,请添加。我不在乎它是否为空,它仍然像填充一样占用行中的空间(即:填充内容/网格项目)。”

虽然 `auto-fill` 会用尽可能多的列填充行,即使这些列为空,`auto-fit` 的行为也略有不同。
`auto-fit` 也会随着视口宽度的增加而用更多列填充行,但唯一的区别是新添加的列(以及与其相关的任何列间隙)都将被折叠。网格检查器是可视化此操作的绝佳方法。您会注意到,当您关注网格线编号时,列正在被添加,这些编号将随着视口宽度的增加而增加。

`auto-fit` 行为:“使您拥有的任何列都适应可用空间。根据需要扩展它们以适应行的大小。空列不得占用任何空间。通过扩展填充的(即:填充内容/网格项目)列以适应可用的行空间来更好地利用该空间。”

这里一个有用的提示是,在这两种情况下添加的列(无论是否折叠)都不是隐式列——这在规范中具有特定含义。在我们的例子中,我们以与声明您希望 12 列相同的方式在显式网格中添加/创建列,例如。因此,列号 `-1` 将用于定位此网格的末尾,如果您在隐式网格中创建列,则它不会这样做。感谢 Rachel Andrew 提供此提示。

总结

仅当行足够宽以容纳更多列时,`auto-fill` 和 `auto-fit` 用于调整列大小的差异才会显现。

如果您使用 `auto-fit`,则内容将拉伸以填充整个行宽。而对于 `auto-fill`,浏览器将允许空列像其非空邻居一样占用行中的空间——即使它们没有网格项目,它们也将被分配一部分空间,从而影响后者的尺寸/宽度。

您想要或更喜欢哪种行为完全取决于您。我还没有想到 `auto-fill` 比 `auto-fit` 更合理的使用场景。您有这样的使用场景吗?如果您有,请随时在下面的评论中分享。