原文地址:CSS Lists, Markers, And Counters 在CSS中,样式列表的种类远比我们想象得要多。本文,会首先讲解CSS的列表,然后把目光转移到CSS列表规范中的一些有趣特性——标记和计数器。
在CSS中,列表具有特定的属性,为我们提供了标准的列表样式。例如,无序列表的列表项符号是圆点,有序列表的是编号。我之所以会去详细研究列表,主要来源于为MDN整理::marker
伪元素的文档。这个伪元素在已经发布的Firefox 68版本中所支持,通过使用::marker
伪元素,我们可以用列表做一些有趣的事情。
虽然我们经常使用列表,但可能从来没有多加细想,很多东西都可以合乎逻辑的被标记为列表项。尽管通过有序列表<ol>
可以很自然的描述分布指令或排序元素,但是很多东西天生更适合用无序列表<ul>
来描述。例如,非常常用的用法是,在站点上标记一系列目标用于导航。
接下来,我们先了解清楚在CSS中什么是列表。
列表和其它CSS属性一样,也有很多初始值,而这些初始值体现了列表的特征。首先,要定义一个元素为列表项,就需要设置display
属性为list-item
。这会生成一个带标记盒子的块级盒子,而标记盒子中放有列表项符号。
列表很早就在CSS规范中定义了,而我们目前所使用的列表特性绝大部分来自CSS2规范。其定义如下:当一个元素设置了display:list-item
,就会生成一个包含元素内容的主块级盒子,和一个包含列表项符号的标记盒子,其中属性list-style-type
和list-style-image
决定了列表项符号的样式。
主块级盒子是元素的主要盒子,其包含了列表项的所有子节点,包括子节点中的标记符。然后,标记盒子相对于主块级盒子排列。另外,规范还定义了背景色background-color
只能作用在主块级盒子上,而不能作用在标记盒子上。标记盒子的list-style-type
属性有以下预设值:
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
在Level 3规范中,表现结果和display:list-item
一样的还有其它display
值。其引用了很多CSS2.1规范中的属性和属性值,但是将list-item
关键字描述为:可以让元素生成::marker
伪元素的属性。
Level 3规范还引入了通过设置两个值来定义行内列表项的功能,即display:inline list-item
,但这还未被浏览器所实现。
若想在非列表项上创建标记盒子,最有效的方式就是给HTML元素设置display
为list-item
,虽然从语义上还不能作为列表项,但是其在视觉表现上如同列表项,且具有::marker
伪元素。在某些场景下,::marker
伪元素对于设置了display: list-item
的元素还是很好用的。
在CSS2.1规范中的display
部分扩展并阐明了列表的基本行为,然而,还有一个规范详细定义了列表的样式—— CSS Lists Specification Level 3。该规范详细说明了,通过display:list-item
创建的有序列表元素和默认的计数器一起使用时所生成的标记盒子特性。通过这些特性可以实现一些潜在功能。
::marker
伪元素可以实现让列表项符号和列表项内容分开设置,这在CSS的早期版本中并非不能实现。在早期,如果要改变ul
或li
中内容的颜色或字体大小,同样也会改变标记符号的的颜色和字体大小,为了设置颜色不一的文本和标记符这样的简单行为,就需要将文本由一个span
元素包裹或使用标记图像。
ul {
color: #00b7a8;
}
ul span {
color #333;
}
若通过::marker
伪元素,实现以上效果最简单的方式如下:
ul {
color: #333;
}
ul ::marker {
color: #00b7a8;
}
或许还想为有序列表的标记符设置字体大小和字体:
ol ::marker {
font-size: 200%;
color: #00b7a8;
font-family: "Comic Sans MS", cursive, sans-serif;
}
(查看示例,请在支持::marker
的浏览器中打开,如Firefox 68)
另外,还可以在非列表项上使用::marker
伪元素。下面的代码中,将h1
设置为display:list-item
,即生成了一个标记盒子,也就是::marker
伪元素。
h1 {
display: list-item;
}
h1::marker {
content: "emoji";
}
(查看示例,请在支持::marker
的浏览器中打开,如Firefox 68)
对于::marker
伪元素来说,只能应用一小部分CSS属性,其包括字体属性和颜色,以及在上述示例中所使用的content
属性——用于生成内容。
::marker
伪元素中的content
属性在规范中是最近添加的,但在Firefox 68中也已支持。这意味着可以在::marker
伪元素中操作文本,当结合计数器使用时,其为标记符号的格式化提供了可能性。
对于不支持::marker
伪元素的浏览器,就会显示常规的标记符号。遗憾的是,目前无法通过功能查询来检测选择器对::marker
伪元素的支持,尽管已经有一个关于将其添加到规范中的ISSUE。这意味着,无法针对不支持的浏览器环境区分处理。大多数情况下,回退到常规的标记符将会是一个合理的解决方案。
有序列表的编号是通过CSS计数器实现的,因此,CSS列表规范中也描述了计数器。结合::marker
伪元素,我们可以通过创建计数器来提供一些有用的功能。这些计数器也可用在常规生成的内容中。
如果我有一个步骤列表,以及需要写下"Step 1","Step 2"等等,可以在标记符中使用content
生成内容,并添加list-item
计数器(内置计数器)。如下:
::marker {
content: "Step " counter(list-item) ": ";
}
(查看示例,请在支持::marker
的浏览器中打开,如Firefox 68)
如果需要给嵌套列表进行编号,常用的方法是先给最外层列表设置一个整数(1)
。接着子项设置为(1.1, 1.2)
以及子项的子级设置为(1.1.1,1.1.2)
等等。现在可以使用更多计数器功能来实现此目的。
嵌套html列表时,最终会有多个同名的计数器彼此嵌套,使用counter()
函数便可以访问嵌套的计数器。
在下面的代码中,我们通过使用counter()
函数来格式化列表标记。counters()
函数的第一个参数是计数器名,这里使用的是内置计数器list-item,第二个参数是个字符串——用于连接输出的计数器(这里使用的是一个.
符号)。最后,我们添加了一个:
符号在计数器函数的外部,这样计数函数输出时,一个:
符号就可以将内容和编号分开。
::marker {
content: counters(list-item,'.') ':';
color: #00b7a8;
font-weight: bold;
}
(查看示例,请在支持::marker
的浏览器中打开,如Firefox 68。可以试着将字符串.
符号改为其他内容,看其输出是如何变化的)
counter()
函数仅适用于最内层的计数器,如在第一个例子中用于写出步骤列表编号。因此,当我们有一组嵌套列表时,用counter()
函数来实现当前层级相关的计数。
counters()
函数实际上可以实现整个嵌套列表的计数,并且可以用字符串来连接各分支上的计数。例如你有一组计数为2的列表项(嵌套在计数为4的列表项里),则该分支包含:
可以通过一下代码在标记中输出4.2
。
::marker {
content: counters(list-item,'.');
}
计数器可用于非列表项,例如用于输出标记 (这种情况下需要给元素设置display: list-item
),或输出常规内容。计数器广泛用于书籍编写中,以便用章节和图形编号来量化其他内容。在网上没有理由不采取类似的方法,特别是对于较长的文章。
在CSS列表规范中,用于计数器的CSS属性有:
counter-set
counter-reset
counter-increment
这些属性是如何用于非列表项的,可以查看以下示例。
首先,我们通过设置counter-reset
属性在body
元素上创建一个计数器。counter-reset
和counter-set
在功能上非常相似。如果指定名称的计数器尚不存在,counter-reset
将创建一个新的计数器,但如果存在对应名称的计数器,则还将创建如上所述的嵌套计数器。而counter-set
则是,如果没有指定名称的计数器,则该属性将仅创建一个新的计数器。对于这个示例来说,两个属性都可以正常工作。但是,counter-set
的浏览器兼容性没有counter-reset
好,所以我们采用更实用的属性counter-reset
。
body {
counter-reset: heading-counter;
}
现在,我们有了一个计数器,然后可以在h2
选择器上使用counter-increment
。每次选择器匹配时,计数就会递增一次。
h2 {
counter-increment: heading-counter;
}
为了查看结果,我们需要将内容输出到文档中,可以通过给标题的::before
伪元素设置content
来生成内容。
h2::before {
content: counter(heading-counter) ": ";
color: #00b7a8;
font-weight: bold;
}
(查看示例)
或者,我们可以将h2
元素设置list-item
,然后通过使用::marker
伪元素来实现上述结果。如之前所述,::marker
伪元素会有浏览器的支持限制,对于上述示例而言,在Firefox中可以看到计数器将作为标题的标记符,而在其他浏览器将仅仅显示原始默认的标记符。
h2 {
display: list-item;
}
h2::marker {
content: counter(heading-counter) ": ";
color: #00b7a8;
font-weight: bold;
}
(查看示例,请在支持::marker
的浏览器中打开,如Firefox 68)
可以使用CSS计数器来实现一些交互——你可能认为需要通过JavaScript来实现。
现在有一个包含许多必填字段的表单,可以在CSS中用:required
伪类来标记必填字段,并且可以通过:invalid
伪类检测无效字段。这意味着我们可以检测到必填和无效字段,通过计数器增加计数,然后将其生成为内容输出。
(查看示例)
这在实际中有多高的使用性是值得商榷的,对于上面例子中计数器生成的值,最大的作用也就是将其在输出内容中展示。另外,对于某些屏幕阅读器而言,生成的内容可能无法访问,因此任何超过限制的用法都需要确保有访问该信息的其他方式。想了解有关可访问性和生成内容的更多详细信息,可阅读"CSS生成内容的可访问性支持"和"CSS内容属性在屏幕阅读器上的兼容性"。
但是,这也证明了计数器不仅仅能实现简单的列表编号,它还可以实现更有用的东西。可能有一天,计数器的相关知识确实能派上用场,能够解决你正在研究的一些问题。
尽管以上所描述的所有内容都可以在CSS List规范中找到,但是在样式列表方面还有很多内容需要讲述。可访问以下链接来找到相关内容的更多描述信息。
::marker
counter-set
counter-reset
counter-increment