使用 CSS 计数器
CSS 计数器让你能够根据内容在文档中的位置来调整其外观。例如,你可以使用计数器自动为网页中的标题编号,或者更改有序列表的编号。
计数器本质上是由 CSS 维护的变量,其值可以通过 CSS 规则增加或减少,这些规则会跟踪它们被使用了多少次。以下几点会影响元素的计数器值:
- 计数器可以从父元素继承,或从前一个兄弟元素接收。
- 新计数器通过
counter-reset属性实例化。 - 计数器通过
counter-increment属性递增。 - 计数器通过
counter-set属性直接设置为一个值。
你可以定义自己的命名计数器,也可以操作为所有有序列表默认创建的 list-item 计数器。
使用计数器
要使用计数器,必须首先使用 counter-reset 属性将其初始化为一个值。计数器的值可以使用 counter-increment 属性来增加或减少,也可以使用 counter-set 属性直接设置为一个特定的值。计数器的当前值通过 counter() 或 counters() 函数显示,通常在伪元素的 content 属性中使用。
计数器只能在生成盒子的元素上设置、重置或递增。例如,如果一个元素被设置为 display: none,那么对该元素的任何计数器操作都将被忽略。
计数器的属性可以使用样式限制来限定作用域于特定元素,这在 contain 属性中有更详细的描述。
操作计数器的值
要使用 CSS 计数器,必须首先使用 counter-reset 属性将其初始化为一个值。该属性也可以用来将计数器值更改为任何特定的数字。
下面我们将一个名为 section 的计数器初始化为默认值 (0)。
counter-reset: section;
你也可以初始化多个计数器,并可选择为每个计数器指定一个初始值。下面我们将 section 和 topic 计数器初始化为默认值,并将 page 计数器初始化为 3。
counter-reset: section page 3 topic;
一旦初始化,计数器的值可以使用 counter-increment 来增加或减少。例如,下面的声明会在每个 h3 标签上将 section 计数器加一。
h3::before {
counter-increment: section; /* Increment the value of section counter by 1 */
}
你可以在计数器名称后指定递增或递减的量。它可以是正数或负数,如果没有提供整数,则默认为 1。
除了递增或递减外,计数器也可以使用 counter-set 属性显式地设置为一个值。
.done::before {
counter-set: section 20;
}
计数器的名称不能是 none、inherit 或 initial;否则该声明将被忽略。
显示计数器
计数器的值可以使用 counter() 或 counters() 函数在 content 属性中显示。
例如,下面的声明使用 counter() 为每个 h3 标题添加前缀文本 Section <number>:,其中 <number> 是十进制计数器的值(默认显示样式)。
body {
counter-reset: section; /* Set a counter named 'section', and its initial value is 0. */
}
h3::before {
counter-increment: section; /* Increment the value of section counter by 1 */
content: "Section " counter(section) ": "; /* Display counter value in default style (decimal) */
}
当嵌套级别的编号不包含父级的上下文时,使用 counter() 函数。例如,这里每个嵌套级别都从 1 重新开始:
1 One 1 Nested one 2 Nested two 2 Two 1 Nested one 2 Nested two 3 Nested three 3 Three
当嵌套级别的计数必须包含父级的计数值时,使用 counters() 函数。例如,你可以用它来布局章节,如下所示:
1 One 1.1 Nested one 1.2 Nested two 2 Two 2.1 Nested one 2.2 Nested two 2.3 Nested three 3 Three
counter() 函数有两种形式:counter(<counter-name>) 和 counter(<counter-name>, <counter-style>)。生成的文本是伪元素作用域内给定名称的最内层计数器的值。
counters() 函数也有两种形式:counters(<counter-name>, <separator>) 和 counters(<counter-name>, <separator>, <counter-style>)。生成的文本是指定伪元素作用域内所有同名计数器的值,从最外层到最内层,由指定的字符串 (<separator>) 分隔。
对于这两种方法,计数器都以指定的 <counter-style> 渲染(默认为 decimal)。你可以使用任何 list-style-type 值或你自己的自定义样式。
反向计数器
反向计数器是指倒数(递减)而非正数(递增)的计数器。反向计数器通过在 counter-reset 中命名计数器时使用 reversed() 函数表示法来创建。
反向计数器的默认初始值等于元素数量(与默认值为 0 的普通计数器不同)。这使得实现一个从元素数量倒数到 1 的计数器变得很容易。
例如,要创建一个名为 section 的具有默认初始值的反向计数器,你可以使用以下语法:
counter-reset: reversed(section);
当然,你也可以指定任何你喜欢的初始值。
通过为 counter-increment 指定一个负值来减少计数器的值。
备注: 你也可以使用 counter-increment 来递减一个非反向计数器。使用反向计数器的主要好处是其默认的初始值,以及 list-item 计数器会自动递减反向计数器。
计数器的继承和传播
每个元素或伪元素在该元素的作用域内都有一组计数器。集合中的初始计数器从元素的父元素和前一个兄弟元素接收。计数器值从前一个兄弟元素的最后一个后代、最后一个兄弟元素或父元素接收。
当一个元素声明一个计数器时,该计数器会嵌套在从父元素接收的同名计数器内部。如果父元素没有同名计数器,则该计数器会直接添加到元素的计数器集合中。从前一个兄弟元素接收的同名计数器会从计数器集合中移除。
counter() 函数检索所提供名称的最内层计数器。而 counters() 函数检索具有给定名称的整个计数器树。
在下面的例子中,我们演示了一个名为 primary 的继承计数器和一个名为 secondary 的兄弟计数器。所有的 <div> 元素都使用 counters() 函数显示它们的计数器。请注意,所有计数器都是使用 counter-reset 属性创建的,并且没有一个计数器被递增过。
<section>
counter-reset: primary 3
<div>A</div>
<div>B</div>
<div>C</div>
<div class="same-primary-name">D</div>
<span> counter-reset: primary 6</span>
<div>E</div>
<div class="new-secondary-name">F</div>
<span> counter-reset: secondary 5</span>
<div>G</div>
<div>H</div>
<div class="same-secondary-name">I </div>
<span> counter-reset: secondary 10</span>
<div>J </div>
<div>K</div>
<section></section>
</section>
/* create 'primary' counter on divs' parent */
section {
counter-reset: primary 3;
}
div::after {
content: " ('primary' counters: " counters(primary, "-", style)
", 'secondary' counters: " counters(secondary, "-", style) ")";
color: blue;
}
/* create new 'primary' counter */
.same-primary-name {
counter-reset: primary 6;
}
/* create 'secondary' counter on div 'F' */
.new-secondary-name {
counter-reset: secondary 5;
}
/* override the sibling 'secondary' counter */
.same-secondary-name {
counter-reset: secondary 10;
}
section 元素初始化了一个名为 primary、值为 3 的计数器,所有子 <div> 都接收了这个继承的 primary 计数器。元素 'D' 创建了一个新的 primary(值为 6)计数器,它嵌套在从父元素接收的计数器中,所以该元素有两个名为 primary 的计数器,值分别为 3 和 6。
元素 'F' 首次创建了 secondary(值为 5)计数器,并将其传递给下一个兄弟元素 'G'。元素 'G' 将计数器传递给下一个元素 'H',依此类推。接下来,元素 'I' 创建了一个同名为 secondary(值为 10)的新计数器,但它丢弃了从前一个兄弟元素 'H' 接收的 secondary(值为 5)计数器,并将其自己的计数器传递给 'J'。
counter-set 和 counter-reset 的区别
counter-set 属性更新一个现有的计数器,如果不存在同名计数器,则实例化一个新的计数器。counter-reset 属性总是创建一个新的计数器。
在下面的例子中,我们有一个父列表,内部有两个子列表。每个列表项都使用一个名为 'item' 的计数器进行编号。第一个子列表使用 counter-set 属性,第二个子列表使用 counter-reset 属性来更改 'item' 计数器。
<ul class="parent">
<li>A</li>
<li>B</li>
<li>
C (the counter updated using `counter-set`)
<ul class="sub-list-one">
<li>sub-A</li>
<li>sub-B</li>
</ul>
</li>
<li>D</li>
<li>
E (a new counter created using `counter-reset`)
<ul class="sub-list-two">
<li>sub-A</li>
<li>sub-B</li>
<li>sub-C</li>
</ul>
</li>
<li>F</li>
<li>G</li>
</ul>
/* create a new counter for the first time */
.parent {
counter-reset: item 0;
}
/* increment the counter on each list item */
li {
counter-increment: item;
}
/* show numbers on list items */
li::before {
content: counter(item) " ";
}
/* change the existing counter value */
.sub-list-one {
counter-set: item 10;
}
/* change the counter value */
.sub-list-two {
counter-reset: item 0;
}
注意第一个子列表项如何从 11 开始编号,并且编号在父列表中继续。这是因为 counter-set 属性更新了在 .parent 元素上声明的同一个 'item' 计数器。然后注意第二个子列表项如何从 '1' 开始重新编号,并且它之后的父列表项没有延续编号。这是因为 counter-reset 属性创建了一个同名的新计数器,所以父列表项继续使用旧的计数器。
列表项计数器
使用 <ol> 元素创建的有序列表,隐式地拥有一个名为 list-item 的计数器。
与其他计数器一样,对于正向计数器,其默认初始值为 0;对于反向计数器,其默认值为“项目数量”。与作者创建的计数器不同,list-item 会根据计数器是否反向,为每个列表元素自动递增或递减一。
list-item 计数器可用于通过 CSS 操作有序列表的默认行为。例如,你可以更改默认初始值,或使用 counter-increment 来更改列表项递增或递减的方式。
示例
基本示例
此示例在每个标题的开头添加了“Section [计数器的值]:”。
CSS
body {
counter-reset: section; /* Set a counter named 'section', and its initial value is 0. */
}
h3::before {
counter-increment: section; /* Increment the value of section counter by 1 */
content: "Section " counter(section) ": "; /* Display the word 'Section ', the value of
section counter, and a colon before the content
of each h3 */
}
HTML
<h3>Introduction</h3>
<h3>Body</h3>
<h3>Conclusion</h3>
结果
基本示例:反向计数器
此示例与上面的示例相同,但使用了反向计数器。如果你的浏览器支持 reversed() 函数表示法,结果将如下所示:

CSS
body {
counter-reset: reversed(
section
); /* Set a counter named 'section', and its initial value is 0. */
}
h3::before {
counter-increment: section -1; /* Decrement the value of section counter by 1 */
content: "Section " counter(section) ": "; /* Display the word 'Section ', the value of
section counter, and a colon before the content
of each h3 */
}
HTML
<h3>Introduction</h3>
<h3>Body</h3>
<h3>Conclusion</h3>
结果
一个更复杂的例子
计数器不必每次递增时都显示。此示例计算所有链接,但仅当链接没有文本时才显示计数器,作为一个方便的替代方案。
CSS
:root {
counter-reset: link;
}
a[href] {
counter-increment: link;
}
a[href]:empty::after {
content: "[" counter(link) "]";
}
HTML
<p>See <a href="https://www.mozilla.org/"></a></p>
<p>Do not forget to <a href="contact-me.html">leave a message</a>!</p>
<p>See also <a href="https://mdn.org.cn/"></a></p>
结果
嵌套计数器示例
CSS 计数器对于制作大纲列表特别有用,因为在子元素中会自动创建计数器的新实例。使用 counters() 函数,可以在不同级别的嵌套计数器之间插入分隔文本。
CSS
ol {
counter-reset: section; /* Creates a new instance of the
section counter with each ol
element */
list-style-type: none;
}
li::before {
counter-increment: section; /* Increments only this instance
of the section counter */
content: counters(section, ".") " "; /* Combines the values of all instances
of the section counter, separated
by a period */
}
HTML
<ol>
<li>item</li> <!-- 1 -->
<li>item <!-- 2 -->
<ol>
<li>item</li> <!-- 2.1 -->
<li>item</li> <!-- 2.2 -->
<li>item <!-- 2.3 -->
<ol>
<li>item</li> <!-- 2.3.1 -->
<li>item</li> <!-- 2.3.2 -->
</ol>
<ol>
<li>item</li> <!-- 2.3.1 -->
<li>item</li> <!-- 2.3.2 -->
<li>item</li> <!-- 2.3.3 -->
</ol>
</li>
<li>item</li> <!-- 2.4 -->
</ol>
</li>
<li>item</li> <!-- 3 -->
<li>item</li> <!-- 4 -->
</ol>
<ol>
<li>item</li> <!-- 1 -->
<li>item</li> <!-- 2 -->
</ol>
结果
规范
| 规范 |
|---|
| CSS 列表与计数器模块第 3 级 # 自动编号 |