关注“间隙”

Avatar of Patrick Brosset
Patrick Brosset

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

您可能已经了解 CSS gap 属性。它并不完全是新的,但它在去年获得了重要的全新功能:它现在除了 CSS Grid 之外,还在 Flexbox 中起作用。这一点,以及我认为该属性比看起来更复杂的事实,让我想要回顾并准确解释它的工作原理。

让我们仔细看看 gap 及其关联的属性,并了解它们如何以及在何处工作。

所有间隙属性

首先,让我们回顾一下与 gap 相关的所有 CSS 属性。总共有六个。

  • grid-row-gap
  • grid-column-gap
  • grid-gap
  • row-gap
  • column-gap
  • gap

从这个列表中,我们可以忽略前三个属性。grid-* 属性是在 CSS Grid 规范起草初期添加的,后来在 gap 变得更通用时被弃用。浏览器仍然支持这些被弃用的 grid-* 属性(截至本文撰写时),并且仅仅将它们视为不存在 grid- 前缀。因此,grid-gapgap 相同,grid-column-gapcolumn-gap 相同,grid-row-gaprow-gap 相同。

至于其他三个属性,知道 gap 是一个简写,允许您指定其他两个属性,我们实际上只需要知道 row-gapcolumn-gap 的作用。

我们对这些属性的理解取决于我们使用的 CSS 布局类型。让我们首先看看这些选项。

间隙可以在哪里使用?

如果您像我一样,您已在网格布局中使用过间隙,但它们现在也可以在 Flexbox 以及多列布局中使用。让我们回顾一下每种情况。

网格间隙

所有浏览器都支持网格布局中的间隙,并且在这种情况下,它们非常容易理解。

  • row-gap 在行轨道之间引入空间。
  • column-gap 在列轨道之间引入空间。

让我们创建一个具有三列和两行的网格。

.container {
  display: grid;
  grid-template-columns: 200px 100px 300px;
  grid-template-rows: 100px 100px;
}

这给了我们以下网格。

A 3 by 2 grid of yellow boxes, with fix-sized tracks displayed in dashed purple lines.

上图中的线称为**网格线**,它们分隔网格的轨道(行和列)。这些线在网格中并不真正存在——它们是不可见的,没有厚度,并且通常是当我们启用网格检查器时(在 SafariFirefoxEdgeChrome 中)DevTools 显示的内容。

The CSS-Tricks site with DevTools open and docked to the left of the viewport in Firefox. DevTools displays Grid Inspector options and the page contains borders around elements in blue and green to indicate grid track lines.

但是,如果我们开始向网格添加间隙,它就会像这些线开始获得厚度一样工作。

让我们添加一个 20px 的间隙。

.container {
  display: grid;
  grid-template-columns: 200px 100px 300px;
  grid-template-rows: 100px 100px;
  gap: 20px;
}

我们轨道之间的线现在有 20px 厚,因此将网格项推得更远。

A 3 by 2 grid of yellow boxes with 20px gaps between the column and row tracks.

值得注意的是,轨道仍然具有相同的大小(由 grid-template-* 属性定义);因此,网格比没有间隙时更宽更高。

在网格中,row-gap 始终应用于行轨道之间。因此,如果我们在上面的示例中用 row-gap 替换 gap,我们将得到以下结果。

The same 3 by 2 grid with a gap only between the two rows.

column-gap 始终应用于列轨道之间,因此用 column-gap 替换 gap 会产生以下结果。

The same 3 by 2 grid with a gap only between the three columns.

网格很简单,因为默认情况下,列是垂直的,行是水平的,就像表格一样。因此,很容易记住 column-gaprow-gap 应用的位置。

现在,当使用 writing-mode 时,事情会变得稍微复杂一些。网络上的默认书写模式是水平的,从左到右,但也有垂直书写模式,当这种情况发生时,列变为水平,行变为垂直。始终注意 writing-mode,因为它可能使其不那么直观。

这是一个很好的过渡到下一部分,因为列和行在 Flexbox 中获得了新的含义。

Flexbox 间隙

让我们谈谈 Flexbox 布局中的间隙,事情变得有点复杂。我们将使用以下示例。

.container {
  display: flex;
}

默认情况下,这会给我们一个 row flex 容器,这意味着容器内的项目从左到右堆叠在同一水平线上。

A default flex container with six yellow boxes stacked horizontally, from left to right. Each one says flex item in it. A purple border is drawn around each item.

在这种情况下,column-gap 应用于项目之间,而 row-gap 没有任何作用。这是因为只有一行(或一行)。但现在让我们在项目之间添加一些间隙。

.container {
  display: flex;
  column-gap: 10px;
}
The same six yellow flex items on a single line with 10 pixels between them.

现在让我们将容器的 flex-direction 切换到 column,它将项目垂直堆叠,从上到下,使用以下代码。

.container {
  display: flex;
  flex-direction: column;
  column-gap: 10px;
}

以下是发生的情况。

Six yellow rectangles stacked from top to bottom with no gap between them.

间隙消失了。即使 column-gap 在容器处于 row 方向时确实在项目之间添加了空间,但在 column 方向上它也不再起作用。

我们需要使用 row-gap 来取回它。或者,我们可以使用带有单个值的 gap 简写,这将在两个方向上应用相同的间隙,因此在两种情况下都能工作。

.container {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
The same six yellow rectangles stacked vertically, but with 10 pixels of space between them.

因此,概括地说,colum-gap 始终垂直工作(假设默认的 writing-mode),而 row-gap 始终水平工作。这并不依赖于 flex 容器的方向。

但现在看看一个包含换行符的示例。

.container {
  display: flex;
  flex-wrap: wrap;
  column-gap: 40px;
  row-gap: 10px;
  justify-content: center;
}

在这里,如果在一行中没有足够的空间来容纳所有内容,我们允许项目使用 flex-wrap: wrap 换行。

Five yellow boxes that wrap into two lines, where three are on the first line and two are on the bottom line. There are differently sized gaps between them based on the space around them.

在这种情况下,column-gap 仍然垂直应用于项目之间,row-gap 水平应用于两个 flex 行之间。

这与网格之间有一个有趣的区别。列间隙不一定会跨 flex 行对齐。这是因为 justify-content: center,它将项目在其 flex 行内居中。这样,我们可以看到每条 flex 行都是一个独立的布局,其中间隙独立于其他行应用。

多列间隙

多列 是一种布局类型,它使内容在多列之间自动流动变得非常容易,就像您在传统的报纸文章中所期望的那样。我们设置列数并设置每列的大小。

A three-column layout of plain text with a 1 em gap between columns

多列布局中的间隙与网格或 Flexbox 的工作方式略有不同。有三个显着的差异。

  • row-gap 没有效果。
  • column-gap 具有非 0 的默认值。
  • 并且间隙可以设置样式。

让我们分解一下。首先,row-gap 没有效果。在多列布局中,没有行需要分隔。这意味着只有 column-gap 是相关的(gap 简写也是如此)。

其次,与网格和 Flexbox 不同,column-gap 在多列布局中具有 1em 的默认值(而不是 0)。因此,即使根本没有指定间隙,内容的列仍然会视觉上分隔开来。当然可以覆盖默认间隙,但这是一个很好的默认值。

以下是示例所依据的代码。

.container {
  column-count: 3;
  padding: 1em;
}

最后,我们可以在多列布局中设置列之间空隙的样式。我们使用 column-rule 属性,它类似于 border

.container {
  column-count: 3;
  column-gap: 12px;
  column-rule: 4px solid red;
  padding: 12px;
}
The same three columns of plain text, but with a red border between the columns.
column-rule 属性在多列布局中为我们提供了一些样式功能。

浏览器支持

gap 在各方面都得到了很好的支持。在 caniuse 上有更多信息,但概括地说。

因此,总的来说,gap 属性得到了很好的支持,并且在大多数情况下,不需要变通方法。

设置 flex 和 grid 中间隙的样式

在 Flexbox 和 CSS Grid 中使用 gap 进行样式设置将非常有用。不幸的是,目前还没有任何浏览器支持它。但好消息是,它可能在不久的将来实现。这已经在 CSS 工作组中进行了讨论,并且 Firefox 正在开发中。一旦 Firefox 拥有一个可工作的实现以及规范提案,它可能会推动其他浏览器也进行实现。

在此期间,有一些解决方法。

一种方法是给网格容器设置一个背景颜色,然后给项目设置不同的颜色,最后设置间隙以使容器颜色显示出来。

虽然这种方法有效,但意味着我们无法使用间隙在项目之间引入空格。这里的 gap 充当边框宽度。因此,为了更直观地分隔项目,我们需要在项目上使用 paddingmargin,但这并不理想……正如我们将在下一节中看到的那样。

我不能只使用 margin 或 padding 吗?

是的,在大多数情况下,我们也可以使用 margin(和/或 padding)在布局元素之间添加视觉空间。但是 gap 具有多种优势。

首先,间隙是在容器级别定义的。这意味着我们为整个布局定义它们一次,并且它们在布局中一致地应用。使用 margin 需要在每个项目上进行声明。当项目性质不同或来自不同的可重用组件时,这可能会变得复杂。

最重要的是,间隙默认情况下只需一行代码即可正确执行。例如,如果我们试图在弹性项目之间引入一些空间,而不是围绕它们,margin 将需要特殊情况来 移除第一个项目之前的额外 margin 和最后一个项目之后的额外 margin。使用间隙,我们不需要这样做。

如果在每个弹性项目上使用 margin: 0 20px,我们将得到以下结果

但是,如果在容器上使用 gap: 40px,我们将得到以下结果

类似地,在网格布局中,在容器级别定义 gap 比必须在每个项目上定义 margin 并考虑应用于网格边缘的 margin 简单得多,并且提供了更好的结果。

如果在每个网格项目上使用 margin: 20px

如果在网格容器上使用 gap: 40px

空白空间累加

结合前面所说的内容,margingap 不必是互斥的。实际上,有许多方法可以将布局的项目进一步分隔开,并且它们都可以很好地协同工作。

gap 属性只是布局容器中盒子之间创建的空白空间的一部分。marginpadding 和对齐方式都可能会增加 gap 已定义的空白空间。

让我们考虑一个示例,其中我们构建一个简单的弹性布局,具有给定的宽度、一些间隙、一些内容分布(使用 justify-content)以及一些 margin 和 padding。

.container {
  display: flex;
  gap: 40px;
  width: 900px;
  justify-content: space-around;
}
.item {
  padding: 20px;
  margin: 0 20px;
}

假设这段代码产生以下结果

现在,让我们准确地了解项目之间的空白空间是如何创建的

如我们所见,两个连续的弹性项目之间有四种不同类型的空白空间

  • 在两个连续的项目之间,间隙定义了这些项目之间的最小空间。可以有更多空间,就像在本例中一样,但永远不能有更少的空间。
  • Margin 将项目进一步分隔开,但与 gap 不同,它在所有项目的两侧都添加了空间。
  • Padding 在每个项目内部提供一些空间。
  • 最后,并且仅当有足够的剩余空间时,内容分布才会生效,并根据 space-around 值在弹性行中均匀地分布项目。

调试间隙

最后,让我们来讨论一个对我来说非常重要的话题:用于调试间隙的 DevTools 支持。总有一些情况会出现问题,并且知道 DevTools 可以为我们提供支持非常令人欣慰,但我们需要知道在这种情况下哪些工具可以帮助我们。

对于 gap,我可以想到两个可能非常有用的特定功能。

我的间隙是否处于活动状态?

除非我们拼写错误 gap 或提供了无效的值,否则该属性将始终应用于页面。例如,这是正确的

.some-class {
  display: block;
  gap: 3em;
}

它不会做任何事情,但它是有效的 CSS,并且浏览器并不介意 gap 不适用于块级布局。但是,Firefox 具有一个名为 Inactive CSS 的功能,它正是这样做的:它关心应用于有意义的事物的有效 CSS。在这种情况下,Firefox DevTools 会在检查器中显示警告。

我的间隙在哪里?

Chrome 和 Microsoft Edge 也具有一个非常有用的调试间隙的功能。它是通过微软和谷歌之间的合作添加的,旨在在 Chromium(为这两个浏览器以及其他浏览器提供支持的开源项目)中构建布局调试工具。在这些浏览器中,您可以将鼠标悬停在“样式”面板中的各个属性上,并查看它们对页面的影响。

光标悬停在“样式”面板中的 gapjustify-content 属性上,页面上的相应区域会亮起,以指示这些属性在哪里生效。
光标悬停在 marginpadding 属性上,这将突出显示页面相应的盒子模型区域。

就是这样。我希望本文有助于理解 CSS 中间隙工作原理的一些细节。