CSS 列表、标记和计数器
已发表: 2022-03-10 CSS 中的列表具有特定的属性,这些属性为我们提供了我们期望的标准列表样式。 无序列表获得一个列表项目符号,类型为disc
,并且有序列表被编号。 我对更详细地探索列表的兴趣来自于我为 MDN 记录::marker
伪元素所做的一些工作。 这个伪元素在 Firefox 68中发布,今天发布。 有了::marker
伪元素,我们就可以开始用列表做一些有趣的事情了,在本文中,我将进行更多解释。
解构列表
尽管我们在标记中经常使用列表,但您可能没有过多考虑列表。 许多事情可以非常合乎逻辑地标记为列表。 虽然分步说明或排序元素可以自然地由有序列表<ol>
描述,但设计中的许多事物都可以使用无序列表<ul>
来描述。 例如,该元素的一个非常常见的用法是标记导航,因为它是网站上的目的地列表。 对于我们的探索,让我们首先找出 CSS 中的列表到底是什么。
与 CSS 中的许多东西一样,列表具有应用到它们的一些初始值。 这些值使它们看起来像一个列表。 这些特殊值以列表项具有值为list-item
的display
属性的信息开头。 这将创建一个带有附加标记框的块级框。 标记框是添加列表项目符号或编号的位置。
列表很早就在 CSS 中定义,我们今天使用的列表的大部分定义都来自 CSS2。 CSS2 规范对列表项的描述如下:
“具有display: list-item
的元素会为元素的内容生成一个主要块框,并且根据list-style-type
和list-style-image
的值,还可能还有一个标记框作为元素的视觉指示一个列表项。”
主块框是元素的主框,包含所有子框,因为列表项可以包含其他标记。 然后相对于该主框放置标记框。 该规范继续详细说明任何背景颜色将仅在此主框后面,而不是标记后面。 此外,标记可以设置为一系列预定义值之一:
-
disc
-
circle
-
square
-
decimal
-
decimal-leading-zero
-
lower-roman
-
upper-roman
-
lower-greek
-
lower-latin
-
upper-latin
-
armenian
-
georgian
-
lower-alpha
-
upper-alpha
-
none
-
inherit
3 级显示规范定义了display: list-item
以及display
属性的其他可能值。 它引用了 CSS 2.1——就像许多来自 CSS2 的 CSS 属性和值一样——但将list-item
关键字描述为“导致元素生成::marker
伪元素”。
3 级规范还引入了使用display: inline list-item
。 这还没有被浏览器实现。
在非列表项上创建标记框
与display
的其他值一样,为任何 HTML 元素提供list-item
的显示类型是完全有效的(如果您希望在项目上生成::marker
伪元素)。 这不会导致元素在语义上成为列表项,而是只会在视觉上显示为列表项,因此能够具有::marker
。 当我们在下面讨论::marker
伪元素时,您会发现在某些情况下给其他元素display: list-item
可能很有用。
CSS Lists Level 3 Specification: ::marker
和 Counters
display
规范扩展并阐明了我们在 CSS2 中找到的列表的定义,但是,还有一个规范详细定义了列表行为:CSS 列表规范级别 3。由于列表项的基本行为在display
中定义,因此规范详细说明了当某些东西具有display: list-item
时生成的标记框以及在您创建有序列表时默认使用的计数器。 通过这些功能可以访问一些潜在有用的功能。
::marker
伪元素
::marker
伪元素允许您将列表标记作为目标——与列表项的内容分开。 这在以前的 CSS 版本中是不可能的,因此,如果您更改ul
或li
的颜色或字体大小,这也会改变标记的颜色和字体大小。 为了做一些看似简单的事情,比如拥有与文本不同的颜色列表项目符号,将涉及将列表项的内容包装在一个跨度中(或使用图像作为标记)。
ul { color: #00b7a8; } ul span { color #333; }
使用::marker
伪元素,您可能想尝试的最简单的事情是使用与文本颜色不同的项目符号,这意味着您可以使用上面示例中的代码代替:
ul { color: #333; } ul ::marker { color: #00b7a8; }
您可能还想为有序列表上的编号使用不同的大小和font-family
。
ol ::marker { font-size: 200%; color: #00b7a8; font-family: "Comic Sans MS", cursive, sans-serif; }
您可以使用我的 CodePen 示例在支持的浏览器中查看所有这些内容:
请参阅 Rachel Andrew 的钢笔 [带和不带标记的彩色子弹](https://codepen.io/rachelandrew/penVJQyoR)。
您可以在非列表项上使用::marker
伪元素。 在下面的代码中,我设置了一个要display: list-item
。 这给了它一个项目符号,因此给它一个::marker
框来定位。
我已将项目符号更改为使用表情符号:
h1 { display: list-item; } h1::marker { content: ""; }

请参阅 Rachel Andrew 的钢笔 [标题和标记](https://codepen.io/rachelandrew/pen/wLyyMG)。
在上面的示例中,我在标记的规则中使用了生成的内容。 只有一小部分 CSS 属性可用于::marker
。 这些包括字体属性和颜色,但是,它们还包括content
属性,用于包含生成的内容。
将content
添加为::marker
的允许属性是最近的,但是,它包含在 Firefox 实现中。 包含意味着您可以执行诸如在::marker
中包含文本字符串之类的操作。 当您将计数器的使用与::marker
结合使用时,它还为标记的格式提供了额外的可能性。
浏览器支持和回退
对于不支持::marker
伪元素的浏览器,回退是无论如何都会显示的常规标记。 不幸的是,我们目前无法使用功能查询来检测对选择器的支持,例如这个伪元素,尽管已经提出了一个关于将其添加到规范中的问题。 这意味着当你得到支持时,你不能 fork 你的代码来做一件事,如果你没有,你就不能做别的事情。 在大多数情况下,回退到常规标记将是一个合理的解决方案。
计数器
有序列表具有列表编号——这是通过 CSS 计数器实现的。 因此,CSS 列表规范也描述了这些计数器。 我们可以自己访问和创建计数器,结合::marker
伪元素可以为我们提供一些有用的功能。 这些计数器也可以用于常规(非::marker
)生成的内容。

如果我有一个编号的步骤列表(并且我想写出“步骤 1”、“步骤 2”等),我可以通过在我的标记中使用生成的内容并附加list-item
计数器来做到这一点,这代表内置计数器:
::marker { content: "Step " counter(list-item) ": "; }

请参阅 Rachel Andrew 的钢笔 [计数器和标记](https://codepen.io/rachelandrew/pen/BgRaoz)。
嵌套计数器
如果您有嵌套列表,对它们进行编号的常用方法是让顶级项目为整数 (1),然后为子项目 (1.1, 1.2) 及其子项目 (1.1.1, 1.1.2),等等。 您可以通过使用计数器的更多功能来实现这一点。
当你嵌套 HTML 列表时,你最终会得到多个同名的计数器——相互嵌套。 可以使用counters()
函数访问计数器的嵌套。
在下面的代码中,我使用counters()
来格式化我的列表标记,如上所述。 counters()
的第一个参数是要使用的计数器的名称。 我正在使用内置的list-item
计数器。 第二个参数是一个字符串——这是将在输出计数器之间连接的内容(我使用的是.
)。 最后,我在 counter 函数之外但在content
的值内添加了一个:
,这样我的计数器输出将通过冒号与内容分开。
::marker { content: counters(list-item,'.') ':'; color: #00b7a8; font-weight: bold; }
这给了我图像中的输出。 如果您使用的浏览器支持::marker
和计数器,那么您可以在 CodePen 示例中看到它的工作原理——尝试将字符串从.
到其他东西看看它是如何改变输出的。

请参阅 Rachel Andrew 的 Pen [嵌套计数器](https://codepen.io/rachelandrew/pen/VJbwxL)。
counter()
和counters()
有什么区别?
我们在第一个示例中用于写出我们的步骤的counter()
函数仅使用最里面的计数器。 因此,在您有一组嵌套列表的情况下,您将写出与您当前所在级别相关的计数器。
counters()
函数本质上写出了整个分支,并让您有机会在分支中的计数器之间连接一个字符串。 因此,如果您有一个计数器为2
的列表项(它是嵌套在计数器为4
的列表项中的列表的一部分),则该分支包含:
-
4
-
2
您可以使用以下命令在标记中将其输出为4.2
:
::marker { content: counters(list-item,'.'); }
其他元素的计数器
计数器可以用于不是列表的东西——或者输出一个标记——在这种情况下,元素需要有display: list-item
或者输出常规生成的内容。 计数器在书籍制作中被广泛使用,以使章节和图形编号数量更多。 没有理由不在网络上采取类似的方法,特别是对于较长的文章。
CSS 列表规范中定义的处理这些计数器的 CSS 属性是:
-
counter-set
-
counter-reset
-
counter-increment
要了解这些在列表之外是如何工作的,我们可以看一个使用计数器对文档中的标题进行编号的示例。
我需要做的第一件事是为 body 元素上的标题创建一个计数器——准备好使用。 我正在使用counter-reset
属性来执行此操作。 counter-reset
和counter-set
属性非常相似。 如果指定名称的计数器不存在,则counter-reset
属性将创建一个新的计数器,但如果该名称的计数器确实存在,也会创建如上所述的嵌套计数器。 如果没有该名称的计数器,则counter-set
属性只会创建一个新的计数器。 为此,使用任一属性都可以正常工作,但是, counter-set
的浏览器支持不如counter-reset
,所以我采取了实用的路线:
body { counter-reset: heading-counter; }
现在我有了一个计数器,然后我可以在标题选择器上使用counter-increment
属性; 这应该在每次选择器匹配时增加计数器。
h2 { counter-increment: heading-counter; }
要查看该值,我需要将其输出到文档中。 我可以通过使用生成的内容并将其添加到标题before
来做到这一点,如以下 CodePen 示例所示:
h2::before { content: counter(heading-counter) ": "; color: #00b7a8; font-weight: bold; }
请参阅 Rachel Andrew 的 Pen [标题和计数器](https://codepen.io/rachelandrew/pen/gNGjxq)。
或者,我可以将h2
元素变成一个list-item
,然后使用::marker
,如下所示。 如前所述,使用::marker
元素的浏览器支持有限。 在 Firefox 中,您应该看到用作标题标记的计数器,而其他浏览器将显示默认项目符号。
h2 { display: list-item; } h2::marker { content: counter(heading-counter) ": "; color: #00b7a8; font-weight: bold; }
请参阅 Rachel Andrew 的 Pen [标题、标记和计数器](https://codepen.io/rachelandrew/pen/pXWZay)。
表单元素上的计数器
您还可以使用 CSS Counters 实现一些交互性——您可能认为需要 JavaScript 来完成。
我有一个包含许多必填字段的表单。 可以在 CSS 中使用:required
伪类选择所需的状态,并且可以通过:invalid
伪类检测字段尚未完成的事实。 这意味着我们可以检查必填和无效的字段,并增加一个计数器。 然后将其作为生成的内容输出。
请参阅 Rachel Andrew 的 Pen [Counting required form fields](https://codepen.io/rachelandrew/pen/vqpJdM)。
这实际上有多大用处是值得商榷的——考虑到除了将其粘贴到生成的内容中之外,我们无法真正使用该值做任何事情。 还有人担心某些屏幕阅读器无法访问生成的内容,因此任何不仅仅是装饰性的使用都需要确保以其他方式访问该信息。 阅读“CSS 生成内容的辅助功能支持”和最新信息“CSS 内容属性屏幕阅读器兼容性”,了解有关辅助功能和生成内容的更多详细信息。
但是,它表明计数器可以实现比简单编号列表更有用的事情。 也许有一天,知识确实会派上用场,以解决您正在解决的问题。
了解更多
尽管我所描述的所有内容都可以在 CSS 列表规范中找到,但这篇文章与样式列表相去甚远。 您可以在下面的链接中找到有关所描述内容的更多信息。 如果您发现 CSS 计数器的有趣用途,或者可以想到可以使用::marker
的东西,请在评论中添加注释。
-
::marker
-
counter-set
-
counter-reset
-
counter-increment
- “使用 CSS 计数器”,MDN 网络文档
- “使用 CSS 计数器和 CSS 网格进行计数”,CSS 技巧