列表标记后的间隙:你需要知道的一切

Avatar of Šime Vidas
Šime Vidas

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

我正在阅读 Google 的 web.dev 博客上的 “创意列表样式”,并注意到文章中 ::marker 部分的一个代码示例有点奇怪。内置的列表标记是项目符号、序数和字母。::marker 伪元素允许我们为这些标记设置样式,或用自定义字符或图像替换它们。

::marker {
  content: url('/marker.svg') ' ';
}

我注意到的示例使用 SVG 图标作为列表项的自定义标记。但是,在 CSS 值中,url() 函数旁边还有一个空格字符(" ")。此空格的目的是在自定义标记之后插入一个间隙。

当我看到这段代码时,我立刻想知道是否有更好的方法来创建这个间隙。在 content 中追加空格感觉更像是解决方法,而不是最佳解决方案。CSS 提供了 marginpadding 以及其他标准方法来将页面上的元素间隔开。这些属性都不能在这种情况下使用吗?

首先,我尝试用适当的边距替换空格字符

::marker {
  content: url('/marker.svg');
  margin-right: 1ch;
}

这不起作用。事实证明,::marker 只支持 一小组主要与文本相关的 CSS 属性。例如,您可以更改标记的 font-sizecolor,并通过将 content 设置为字符串或 URL 来定义自定义标记,如上所示。但是 marginpadding 属性 不受支持,因此设置它们不会产生任何效果。太令人失望了。

难道空格字符真的是在自定义标记之后插入间隙的唯一方法吗?我需要弄清楚。在研究这个主题的过程中,我有一些有趣的发现,我想在这篇文章中分享。

添加填充和边距

首先,让我们确认 <ul><li> 元素上的 marginpadding 的作用。为此,我创建了一个测试页面。拖动相关的滑块,并观察对列表标记两侧间距的影响。提示: liberally 使用“重置”按钮将所有控件重置为其初始值。

注意:浏览器会将 40px 的默认 padding-inline-left 应用于 <ol><ul> 元素。逻辑 padding-inline-start 属性等效于从左到右内联方向书写系统中的物理 padding-left 属性。在本文中,为了简单起见,我将使用物理属性。

如您所见,<li> 上的 padding-left 会增加列表标记之后的间隙。其他三个属性控制标记左侧的间距,换句话说,就是列表项的缩进。

请注意,即使列表项的 padding-left0px,标记之后仍然存在最小间隙。这个间隙无法用 marginpadding 缩小。最小间隙的精确长度取决于浏览器。

First three properties: UL margin-left, UL padding-left, LI margin-left. Fourth property: LI padding-left.
前三个属性将整个列表项(包括标记)推向右侧。第四个属性只将列表项的内容推向右侧。

总而言之,列表项的内容的位置距离标记有一个浏览器特定的最小距离,并且可以通过在 <li> 上添加 padding-left 来进一步增加此间隙。

接下来,让我们看看当我们将标记定位到列表项内部时会发生什么。

将标记移动到列表项内部

list-style-position 属性接受两个关键字:outside(默认值)和 inside,它将标记移动到列表项内部。后者对于创建具有全宽列表项的设计很有用。

A grocery list. Each item has a thin bottom border that extends from the left to the right edge of the list.
列表标记定位在列表项内部,因此列表项的下边框可以延伸到列表框的左边缘

如果标记现在位于列表项内部,这是否意味着 <li> 上的 padding-left 不会再增加标记之后的间隙?让我们来了解一下。在我的测试页面上,通过复选框打开 list-style-position: inside。这四个 paddingmargin 属性受此更改的影响如何?

如您所见,<li> 上的 padding-left 现在会增加标记左侧的间距。这意味着我们已经失去了通过 padding-left 来增加标记之后的间隙的能力。在这种情况下,能够在 ::marker 本身添加 margin-right 非常有用,但正如我们之前所说,那并不可行。

The four properties: UL margin-left, UL padding-left, LI margin-left, LI padding-left.
所有四个属性都会将整个列表项推向右侧。最小间隙无法通过标准方法增加。

此外,在 Chromium 中有一个错误,导致在切换到 inside 定位后,标记之后的间隙增加到原来的三倍。默认情况下,间隙的长度约为文本大小的三分之一。因此,在默认的 font-size16px 时,间隙约为 5.5px。在切换到 inside 后,间隙在 Chrome 中增长到完整的 16px。此错误会影响 disccirclesquare 标记,但 不会影响序数标记

下图显示了三种主要浏览器在 macOS 上对外部和内部定位的列表标记的默认渲染。为了方便起见,我已经将所有列表项在其标记上水平对齐,以便于比较间隙大小的差异。

Six list items with varying gaps between the marker and text.
只有 Firefox 保持了两种标记定位模式之间的相同间隙大小。这可以被认为是一个浏览器互操作性(interop)问题。

总而言之,切换到 list-style-position: inside 会带来两个问题。我们无法再通过 <li> 上的 padding-left 来增加间隙,并且间隙大小在不同浏览器之间不一致。

最后,让我们看看当我们用自定义标记替换默认列表标记时会发生什么。

切换到自定义标记

有两种方法可以定义 自定义标记

  • list-style-typelist-style-image 属性
  • ::marker 伪元素上的 content 属性

content 属性功能更强大。例如,它允许我们使用 counter() 函数访问列表项的序数(隐式 list-item 计数器),并用自定义字符串装饰它。

不幸的是,Safari 尚未支持 ::marker 上的 content 属性(WebKit 错误)。出于这个原因,我将使用 list-style-type 属性来定义自定义标记。您仍然可以使用 ::marker 选择器来设置通过 list-style-type 声明的自定义标记的样式。::marker 的这方面在 Safari 中受支持。

任何 Unicode 字符都有可能用作自定义列表标记,但只有少数字符在其官方名称中实际上包含“项目符号”,所以我想在这里将它们列出来供参考。

字符名称代码点CSS 关键字
项目符号U+2022disc
三角形项目符号U+2023
连字符项目符号U+2043
黑色左向项目符号U+204C
黑色右向项目符号U+204D
反向项目符号U+25D8
白色项目符号U+25E6circle
反向旋转花卉心形项目符号U+2619
旋转加粗黑色心形项目符号U+2765
旋转花卉心形项目符号U+2767
圆圈白色项目符号U+29BE
⦿圆圈项目符号U+29BF

注意:CSS square 关键字在 Unicode 中没有相应的“项目符号”字符。最接近的字符是黑色小型方块(▪️)表情符号(U+25AA)。

现在让我们看看当我们将默认列表标记替换为 list-style-type: "•"U+2022 项目符号)时会发生什么。这与默认项目符号是相同的字符,因此不应该有任何重大的渲染差异。在我的测试页面上,打开 list-style-type 选项,并观察对标记的任何更改。

如您所见,有两个重大变化

  1. 标记之后不再存在最小间隙。
  2. 项目符号变小了,就好像它是在较小的 font-size 下渲染的一样。

根据 CSS 计数器样式级别 3,默认列表标记(disc)应该“类似于 • U+2022 项目符号”。似乎浏览器会增大默认项目符号的大小以使其更易于阅读。Firefox 甚至为标记使用了一种特殊的字体,-moz-bullet-font

:marker selected in the inspector. Fonts used: -moz-bullet-font.
Firefox 的 DOM 检查器中的“字体”窗格显示了特殊字体。

CSS 可以解决尺寸过小的问题吗?在我的测试页面上,打开标记样式,观察当你改变标记的 `font-size`、`line-height` 和 `font-family` 时会发生什么。

正如你所看到的,增大 `font-size` 会导致自定义标记在垂直方向上错位,并且无法通过减小 `line-height` 来纠正。`vertical-align` 属性可以轻松解决这个问题,但它不支持 `::marker`。

但是你注意到改变 `font-family` 可以使标记变大吗?尝试将其设置为 `Tahoma`。这可能是一个足够好的解决尺寸过小问题的办法,尽管我没有测试过哪种字体在主流浏览器和操作系统上效果最佳。

你可能还注意到,当你将标记定位在列表项内部时,Chromium 的错误不再出现。这意味着自定义标记可以作为该错误的解决方法。这也导致了我的主要问题,也是我开始研究这个主题的原因。**如果你定义一个自定义标记并将其定位在列表项内部,标记后面没有间隙,并且没有办法用标准方法插入间隙。**

  1. 自定义标记后面没有最小间隙。
  2. ::marker 不支持 `padding` 或 `margin`。
  3. <li> 上的 `padding-left` 不会增加间隙,因为标记是定位在 `inside` 的。

总结

以下是本文中提到的所有关键事实的总结

  1. 浏览器会对 `<ul>` 和 `<ol>` 元素应用默认的 `padding-inline-start` 为 `40px`。
  2. 内置列表标记(`disc`、`decimal` 等)后面有最小间隙。自定义标记(字符串或 URL)后面没有最小间隙。
  3. 可以通过向 `<ul>` 添加 `padding-left` 来增加间隙的长度,但这只有在标记定位在列表项外部(默认模式)时才有效。
  4. 自定义字符串标记的默认尺寸比内置标记小。更改 `::marker` 上的 `font-family` 可以增大其尺寸。

结论

回顾文章开头的代码示例,我认为我现在明白为什么 `content` 值中有一个空格字符。没有比这更好的方法来在 SVG 标记后面插入间隙了。这是一个必要的解决方法,因为无论使用多少 `margin` 和 `padding`,都无法在定位在列表项内部的自定义标记后面创建间隙。`::marker` 上的 `margin-right` 可以轻松做到这一点,但它不支持。

在 `::marker` 添加对更多属性的支持之前,Web 开发人员往往别无选择,只能隐藏标记并使用 `::before` 伪元素来模拟它。我最近不得不自己这样做,因为我无法更改标记的 `background-color`。希望我们不用等太久就能拥有更强大的 `::marker` 伪元素。