图解CSS:奇妙的CSS计数器世界(Part3)

发布于 大漠

奇妙的CSS计数器世界主要分为三个部分,在第一部分中介绍了 list-style 的基本使用之外,主要是向大家阐述了如何使用 @counter-style 实现自定义的列表项标记符(list-style-type);在第二部分中介绍列表标记框,即伪元素 ::marker的使用以及可用于::marker上的CSS属性。至此,我们可以在::marker::before::after三个伪元素上的content生成内容,来构建更具个性化的列表项标记符。虽然如此,要实现具有计数(或者说编号)的列表项符号样式,还是需要CSS的计数器来助力,即counter-resetcounter-incrementcounter()counters()函数。在接下来的内容,主要围绕着这几个属性展开。感兴趣的请继续往下阅读。

使用计数器自动编号

我们都知道,有序列表<ol>的每个列表项都是有编号的,比如:

<!-- HTML -->
<ol>
    <li class="list-item">Ordered list item</li>
    <li class="list-item">Ordered list item</li>
    <li class="list-item">Ordered list item</li>
    <li class="list-item">Ordered list item</li>
</ol>

这是通过CSS计数器的方式实现的。

CSS 计数器是一种特殊的数字跟踪器,主要用于对 CSS 中的列玥项进行自动编号。每个元素都有一个零个或多个计数器的集合,它们通过文档树以类似继承属性值的方式被继承下来。比如:

<!-- HTML -->
<ol>
    <li class="list-item">Ordered list item</li>
    <li class="list-item">
        Ordered list item
        <ol>
            <li class="list-item">
                Ordered list item
                <ol>
                    <li class="list-item">Ordered list item</li>
                </ol>
            </li>
        </ol>
    </li>
</ol>

CSS 中的每个计数器都有一个名称和创建者,用来识别计数器,还有一个整数值。可以通过计数器属性counter-incrementcounter-setcounter-reset来创建计数器,也可以通过counter()counters()函数来调用已创建的计数器。

CSS中解决一个给定元素上的计数器值是一个多步骤的过程:

  • ①:现有的计数器是从以前的元素中继承的,即 使用隐式的计数器list-item
  • ②:新的计数器被实例化(counter-reset),即 使用 counter-reset 声明一个计数器名称(<counter-name>
  • ③:计数器的值被递增(counter-increment),即 使用counter-increment 指定计数器(<counter-name>)值是被递增的
  • ④:计数器的值被明确地设置(counter-set),即 使用counter-set指定计数器的值是一个确切的值
  • ⑤:使用计数器的值(counter()counters),即使用counter()counters()函数调用counter-reset已创建的计数器(<counter-name>

注意,UA(客户端)可能对计数器的最大或最小值有一定的限制。如果一个计数器重置、设置或增量会将值推到该范围之外,那么该值必须在该范围内。

正如前面示例所示,自己创建的CSS计数器,需要结合::marker::before::aftercontent一起使用。另外,在CSS中的计数器又分为 隐式计数器显式计数器(即自定义计数器)两种。我们先来看第一种。

隐式计数器

CSS的列表项lidisplay值为list-item,除此之外,还可以给元素(非li元素)设置display的值为list-item。此时,这些元素(即li元素和显式设置了displaylist-item的元素)会自动创建一个名为list-item的计数器(<counter-name>的名称为list-item),它在生成列表项的默认标记字符串时使用(list-style-type)。

  • ullist-style-type 的值是 disc
  • ollist-style-type 的值是 decimal
  • displaylist-item的元素的list-style-type的值和ullist-style-type值相同,即disc

但只有ol有编号的效果。不过,我们可以使用counter()函数引用list-item计数器名称(隐式计数器)对非ol的列表也有编号的效果,但要结合::marker::before::aftercontent一起使用。

CSS的content(生成内容)属性可以使用<counter>,也就是counter()counters()函数,使用计数器。

来看一个简单地示例,比如说我们想构建一个有编号的步骤列表,比如“Step1”、“Step2”、“...”至“StepN”(N是一个数值),我们可以像下面这样来使用:

li::marker {
    content: "Step" counter(list-item) ":";
}

如上图所示,所有的li的编号都是以字符Step开头加上列表项的编号,即使是嵌套的列表项也是如此。但如果你对嵌套列表,在编号上有所差异,比如:

  • 一级列表项的编号为整数,比如Step1Step2、依此类推
  • 二级列表项的编号在一级列表项整数编号基础上追加.符号,然后紧跟是整数,比如Step1.1Step1.2、依此类推
  • 三级列表项的编号在二级列表项基础上继续扩展,比如Step1.1.1Step1.1.2、依此类推
  • 四级、五级到N级列表项的编号按上面的方式类推

对于这样的效果,可以通过counters()函数来实现:

li::marker {
    content: "Step" counters(list-item, ".") ": ";
}

因为每个列表项都会自动将list-item的计数器增加1,所以具有数字列表样式类型(list-style-type)的连续列表项默认会连续编号,即使将counter-increment设置为其他值,比如,counter-increment: itemnumber 或者 none

li {
    counter-increment: itemnumber; /* 或 none */
}

ol:nth-child(1) li::marker {
    content: "Step" counter(list-item) ":";
}

ol:nth-child(2) li::marker {
    content: "Step" counters(list-item, ".") ":";
}

这样可以保护list-item计数器(隐式计数器)不被旨在处理其他计数器的声明无意覆盖。然而,如果列表项的 counter-increment 明确提到了 list-item 计数器,并指定了一个整数值,比如:

li {
    counter-increment: list-item 2;
}

列表项的起始值就是2了,并列表项编号以2递增:

而且我们可以像下面这样使用来关闭列表项的自动增量:

li {
    counter-increment: list-item 0;
}

counter-increment指定list-items计数器的递增量还可以是负值,比如-1,那么列表项起始编号为-1,并且以-1往后递减:

li {
    counter-increment: list-item -1;
}

显式计数器

显式计数器也被称为自定义计数器,即使用:

  • counter-reset显式创建一个计数器名称<counter-name>
  • counter-increment指定计数器递增值或counter-set指定计数器确切值
  • counter()counters()函数引用已定义的计数器<counter-name>

他对应的HTML结构如下:

<counter-reset>
    <counter-increment>
        counter-increment::marker || counter-increment::before || counter-increment::after
    </counter-increment>
</counter-reset>

我们来看一个简单地示例:

<ol><!-- counter-reset -->
    <li class="list-item">Ordered list item</li> <!-- counter-increment -->
    <li class="list-item">
        Ordered list item
        <ol> <!-- counter-reset -->
            <li class="list-item"> <!-- counter-increment -->
                Ordered list item
                <ol> <!-- counter-reset -->
                    <li class="list-item">Ordered list item</li> <!-- counter-increment -->
                    <li class="list-item">Ordered list item</li>
                    <li class="list-item">Ordered list item</li>
                </ol>
            </li>
            <li class="list-item">Ordered list item</li>
            <li class="list-item">Ordered list item</li>
            <li class="list-item">Ordered list item</li>
        </ol>
    </li>
    <li class="list-item">Ordered list item</li>
    <li class="list-item">Ordered list item</li>
</ol>

显式定义计数器:

ol {
    counter-reset: countername;
}

li {
    counter-increment: countername var(--counter-increment, 1);
}

ol:nth-child(1) li::marker {
    content: counter(countername) ": ";
}

ol:nth-child(2) li::marker {
    content: counters(countername, ".") ": ";
}

调整counter-increment 递增量,效果如下:

显式定义的计数器,如果不使用counter-increment指定已声明的计数器,那将不会具有自动编号的能力:

ol {
    counter-reset: countername;
}

ol:nth-child(1) li::marker {
    content: counter(countername) ": ";
}

ol:nth-child(2) li::marker {
    content: counters(countername, ".") ": ";
}

从上面的示例中不难发现,不管是隐式的计数器还是显式的计数器,都将会用到counter-incrementcounter()counters(),而且显式计数器还离不开counter-reset。那接下来,我们来看看这几个属性如何使用。

计数器相关属性

我们从定义计数器名称开始,即counter-reset属性。

counter-reset

counter-reset 属性主要用来显式创建一个计数器名称,即在一个元素上实例化了新的计数器,并将它们设置为指定的整数值。其语法很简单:

counter-reset: [ <counter-name> <integer>? ]+ | none

该属性接受两个值。其中none是初始值,也将表示该元素不会创建任何新的计数器。它可用于重置隐式的计数器(或重置隐藏在不太具体的规则中定义的计数器)。另个值是[ <counter-name> <integer>? ],其中:

  • <counter-name> 是指主数器的名称,实例化一个给定名称的计数器,它可以是任意有效的<custom-ident>,但一些关键词不能用于该名称中,比如noneunsetinitialinherit
  • <integer>指定计数器的起始值,它可以是正值,也可以是负值。如果未显式指定该值,则是0

注意,counter-reset定义的名称(<counter-name>

如果<counter-name><integer> 同时用于counter-reset属性时,它们之间需要用空格分隔开来。比如:

.list {
    counter-reset: section; 
    /** 相当于 
    * counter-reset: section 0;
    **/
}

h1 {
    counter-reset: heading 1;
}

但如果没有显式使用counter-increment指定已创建的计数器,即使在伪元素::marker(或::before::after)中的content使用counter()调用,计数不会有任何递增,将会是counter-reset中的<integer>值(如果未指定,即是0):

.counter-reset {
    counter-reset: section;
}

.counter-reset > *::before {
    content: counter(section);
}

如果counter-reset属性指定了<integer>值,在未显式使用counter-increment指定counter-reset显式定义的计数器名称,那么都将会是<integer>的值:

.counter-reset {
    counter-reset: section 2;
}

.counter-reset > *::before {
    content: counter(section);
}

另外,counter-reset显式创建计数器时,可以同时创建多个。在创建多个的时候,只需要用空格符分隔开即可:

.counter-reset {
    counter-reset: heading 1 section 2;
}

.counter-reset h1::before {
    content: counter(heading);
}

.counter-reset p::before {
    content: counter(section);
}

我们只改变 HTML的结构,整个效果也将会改变:

<!-- HTML -->
<div class="counter-reset">
    <h1 data-element="h1">Counter-reset</h1>
    <p data-element="p">The counter-reset CSS property resets a CSS counter to a given value.</p>
    <aside data-element="aside">
        <p data-element="p">To change the value of an existing counter use counter-set, or counter-increment.</p>
        <p data-element="p">To change the value of an existing counter use counter-set, or counter-increment.</p>
    </aside>
    <p data-element="p">The counter-reset CSS property resets a CSS counter to a given value.</p>
    <section data-element="section">
        <p data-element="p">To change the value of an existing counter use counter-set, or counter-increment.</p>
        <p data-element="p">To change the value of an existing counter use counter-set, or counter-increment.</p>
    </section>
</div>

/* CSS */
.counter-reset {
    counter-reset: heading 1 section 2;
}

.counter-reset h1::before {
    content: counter(heading);
}

.counter-reset p::before {
    content: counter(section);
}

注意,上面示例使用的是伪元素::beforecontentcounter()函数引用counter-reset定义的计数器,这是因为示例中的元素不是list-item。不具有::marker伪元素!

我想大家已经有一定的体验了,没有counter-increment引用counter-reset,计数器不会有任何递增或递减。大家可以通过点击下面示例中的单选按钮,查看对比效果:

counter-incrementnoneheading section效果对比:

counter-increment 和 counter-set

counter-incrementcounter-set 属性用来控制现有计数器的值(隐式计数器list-itemcounter-reset创建的计数器),即用于将 CSS 计数器的值增加给定的值,也就是说列表项的编号递增(或递减)的值。它们语法和counter-reset类似:

counter-increment: [ <counter-name> <integer>? ]+ | none
counter-set: [ <counter-name> <integer>? ]+ | none

其中none为初始值,表示元素不改变任何计数器的值。

[ <counter-name> <integer>? ] 是一个以空格分隔的两个值:

  • <counter-name> 指的是递增的计数器的名称,一般是隐式计数器list-itemcounter-reset 定义的计数器名称。和 counter-reset属性中的<counter-name>相同
  • <integer> 指的是计数器的值,即递增或递减的值。如果没有显式设置,counter-increment则默认为 1(注意,counter-reset 中的 <integer>默认的值为0),counter-set则默认为0

counter-incrementcounter-set还是略有差异的:

  • counter-increment 设置计数器按<integer>指定的值递增(递减)
  • counter-set 将计数器设置为一个给定的值(<integer>)。它可以操作现有的计数器的值,并且只在该元素上还没有一个给定名称的计数器时才会创建新的计数器

我们先来看counter-increment的使用。假设我们有一个像下面这样的 HTML 结构:

<!-- HTML -->
<div class="counter-reset">
    <h1 data-element="h1">Counter-increment</h1>
    <p data-element="p">The counter-re...</p>
    <aside data-element="aside">To change the value of...</aside>
    <p data-element="p">The counter-reset C...</p>
    <section data-element="section">To change the value ...</section>
</div>

上面代码的DOM结构嵌套关系很简单:

使用 CSS,来创建一个计数器,并且使用counter-increment操作该计数器:

.counter-reset {
    counter-reset: counter-name;
}

.counter-reset * {
    counter-increment: counter-name;
}

.counter-reset *::before {
    content: counter(counter-name);
}

在这个示例中的counter-resetcounter-increment<integer>默认值,分别是01counter-reset<integer>0counter-increment<integer>1)。也就是说,counter-increment会让计数器counter-name(该计数器是由counter-reset创建的)按1往上递增。

从上图可以看出来,div.counter-reset的子元素计数器按1递增,并且初始编号是1(即counter-increment<integer>)。

如果我们在counter-reset定义计数器时,指定<integer>的的值为2

.counter-reset {
    counter-reset: counter-name 2;
}

.counter-reset * {
    counter-increment: counter-name;
}

.counter-reset *::before {
    content: counter(counter-name);
}

可以看出,div.counter-reset的子元素编号从 <integer> + 1 = 2 + 1 = 3开始,并且按1递增。如果在上面的基础上继续改进,即在counter-increment指定计数器的增量值(<integer>)是2

.counter-reset {
    counter-reset: counter-name 2;
}

.counter-reset * {
    counter-increment: counter-name 2;
}

.counter-reset *::before {
    content: counter(counter-name);
}

还有一情况,counter-reset定义计数器时,不显式指定<integer>值,即为默认值0,但显式指定counter-increment<integer>值为2

.counter-reset {
    counter-reset: counter-name;
}

.counter-reset * {
    counter-increment: counter-name 2;
}

.counter-reset *::before {
    content: counter(counter-name);
}

可以看出,div.counter-reset的子元素的初始编号是counter-increment属性指定的<integer>(即m=2)值2,并且按2递增。

同样的,counter-increment<integer>也可以是负值,大家可以通过下面示例感受一下:

调整示例中的滑块,分别改变counter-resetcounter-increment<integer>的值,效果如下:

上面示例都是给div.counter-reset所有子元素指定计数器。我们换过一种方式来,比如说,仅给div.counter-reset的所有<p>元素计数:

.counter-reset {
    counter-reset: counter-name;
}

.counter-reset p {
    counter-increment: counter-name;
}

.counter-reset p::before {
    content: counter(counter-name);
}

这个时候只有p元素才会按指定计数器进行编号:

也可以在下面示例中,尝试调整counter-resetcounter-increment<integer>的值:

如果当前元素上没有一个给定名称的计数器(没有使用counter-reset定义计数器,也没有使用隐式计数器list-item),但直接使用counter-increment指了计数器。此时,counter-increment在给元素做递增(或递减)前给元素实例化了一个给定的名称的新计数器(相当于使用counter-reset定义了一个计数器)

h1 {
    counter-increment: chapter;
}

h1::before {
    content: "H0" counter(chapter);
}

p {
    counter-increment: paragraph;
}

p::before {
    content: counter(paragraph);
}

但我们换过一种方式来使用,把counter-increment直接用在伪元素上,此时counter-increment不会对指定的计数器做递增(或递减)编号:

h1::before {
    content: "H0" counter(chapter);
    counter-increment: chapter;
}

p::before {
    content: counter(paragraph);
    counter-increment: paragraph;
}

效果如下:

但在其父元素上显式使用counter-reset指定该计数器,counter-increment即使用在伪元素上,也能对该计数器做递增或递减:

.counter-reset {
    counter-reset: paragraph;
}

p::before {
    content: counter(paragraph);
    counter-increment: paragraph;
}

更为神奇的是,如果使用counter-increment指定多次,则会成倍数递增(或递减):

h1 {
    counter-increment: chapter;
}

h1::before {
    content: "H0" counter(chapter);
    counter-increment: chapter;
}

p {
    counter-increment: paragraph;
}

p::before {
    content: counter(paragraph);
    counter-increment: paragraph;
}

正如上面示例所示,如果同一计数器名称(<counter-name>)的多个实例出现在属性值中,它们都会按顺序被处理。因此,增量将复合,但只有最后设定的值才生效。

为此,为了避开这些不确定的现象(多个实例化的计数器名称),即复合现象。我们在使用计数器时,尽可能的按这样的结构来使用:

<!-- HTML -->
<counter-reset>
    <counter-increment>
        counter-increment::marker || counter-increment::before || counter-increment::after
    </counter-increment>
</counter-reset>

<!-- 相当于 -->
<div class="counter-reset">
    <div class="counter-increment"></div>
    <!-- .... -->
    <div class="counter-increment"></div>
</div> 
    
/* CSS */
.counter-reset {
    /*显式创建计数器名称*/
    counter-reset: counter-name
}

.counter-increment {
    /* 操作已定义的计数器 */
    counter-increment: counter-name 
}

.counter-increment::before {
    /* 使用指定计数器生成内容 */
    content: counter(counter-name)
}

接下来,我们再来看看嵌套时计数器的表现。

CSS 的计数器是“自嵌套”(Self Nesting)的,在一个元素上实例化一个新的计数器,这个元素从它的父级继承了一个相同名称的计数器,会创建一个相同名称的新计数器,嵌套在现有的计数器中。HTML的列表表现就是这样的,列表可以被嵌套到任意的深度,也不可能为每一层定义唯一命名的计数器counter()函数只会检索元素上给定名称的最内层计数器,而counters()函数则使用包含该元素的所有给定名称的计数器。

我们把上面示例中的 HTML 结构稍作调整:

<div class="counter-reset">
    <h1>...</h1>
    <p>...</p>
    <aside>
        ...
        <p>...</p>
        <p>...</p>
    </aside>
    <p>...</p>
    <section>
        ...
        <p>...</p>
        <p>...</p>
    </section>
</div>

CSS 不做过多变化:

.counter-reset {
    counter-reset: paragraph;
}

.counter-reset p {
    counter-increment: paragraph;
}

.counter-reset p::before {
    content: counter(paragraph);
}

你看到的效果如下:

即使使用counters()替代counter(),我们看到的效果依旧如此:

.counter-reset p::before {
    content: counters(paragraph, ".");
}

同样的,如果改变counter-resetcounter-increment<integer>值,能得到和前面所描述的效果:

.counter-reset {
    counter-reset: paragraph var(--n, 0);
}

.counter-reset p {
    counter-increment: paragraph var(--m, 0);
}

.counter-reset p::before {
    content: counter(paragraph);
}

调整滑块,你将看到下图这样的效果:

正如你所看到的,p元素按顺序计数时,按着它在DOM源出现的顺序按counter-increment指递的值递增或递减。但我们很多时候,希望嵌套计数是像下面这样的方式来计数的:

如果要使用计数器实现上面的效果,除了counter-resetcounter-incrementcounters()几个属性之外还不够,对于结构也有所要求,需要类似像列表嵌套这样:

<ol>
    <li>
        <ol>
            <li>
                <ol>
                    <li></li>
                </ol>
            </li>
            <li></li>
        </ol>
    </li>
    <li></li>
</ol>

我们把上面的示例稍作调整:

<!-- HTML -->
<div class="order-list">
    <h1>Heading...</h1>
    <div class="list-item">
        list item (div)
        <div class="order-list">
            <div class="list-item">
                list item (div)
                <div class="order-list">
                    <div class="list-item">list item (div)</div>
                    <div class="list-item">list item (div)</div>
                </div>
            </div>
            <div class="list-item">list item (div)</div>
        </div>
    </div>
    <div class="list-item">list item (div)</div>
</div>

/* CSS */
.order-list {
    counter-reset: order;
}

.list-item {
    counter-increment: order;
}

.list-item::before {
    content: counters(order, ".");
}

效果如下:

其实,在不改变HTML结构的情况下,也能使用计数器实现嵌套的计数编号,只是稍微复杂一点。比如下面示例:

<!-- HTML -->
<div class="counter-reset">
    <h1 data-element="h1">Counter-increment</h1>
    <p data-element="p">Heading...</p>
    <aside data-element="aside">
        Aside...
        <p data-element="p">Paragraph...</p>
        <p data-element="p">Paragraph...</p>
    </aside>
    <p data-element="p">Paragraph...</p>
    <section data-element="section">
        Section...
        <p data-element="p">Paragraph...</p>
        <p data-element="p">Paragraph...</p>
    </section>
</div>

/* CSS */
.counter-reset {
    counter-reset: paragraph;
}

.counter-reset > :not(h1) {
    counter-increment: paragraph;
}

section {
    counter-reset: section;
}

aside {
    counter-reset: aside;
}

section p {
    counter-increment: section;
}

aside p {
    counter-increment: aside;
}

.counter-reset > :not(h1)::before {
    content: counters(paragraph, ".");
}

section p::before {
    content: counter(paragraph) "." counter(section);
}

aside p::before {
    content: counter(paragraph) "." counter(aside);
}

效果如下:

我们还可以继续简化一下,就是在counter-reset上同时创建多个计数器名称:

.counter-reset {
    counter-reset: level1 level2-1 level2-2;
}

.counter-reset > :not(h1) {
    counter-increment: level1;
}

section p {
    counter-increment: level2-1;
}

aside p {
    counter-increment: level2-2;
}

.counter-reset > :not(h1)::before {
    content: counter(level1);
}

section p::before {
    content: counter(level1) "." counter(level2-1);
}

aside p::before {
    content: counter(level1) "." counter(level2-2);
}

可以像下面这样:

.counter-reset {
    counter-reset: order;
}

.counter-reset > :not(h1) {
    counter-increment: order;
}

section p {
    counter-increment: section;
}

aside p {
    counter-increment: aside;
}

.counter-reset > :not(h1)::before {
    content: counter(order);
}

section p::before {
    content: counter(order) "." counter(section);
}

aside p::before {
    content: counter(order) "." counter(aside);
}

最后来看counter-set属性的效果。counter-setcounter-increment最大不同之处是counter-set指定的是一个固定值,计数器并不会做递增或递减:

.counter-reset {
    counter-reset: order;
}

.counter-reset p {
    counter-set: order;
}


.counter-reset p::before {
    content: counter(order);
}

同样也可以改变counter-set<integer>的值:

counter() 和 counters()

不管是 counter-resetcounter-increment 还是 counter-set,他们只是初始化计数器名称和指定计数器递增(或递减)值,但它们本身没有可见的效果。如果想要得到真的计数的值,是离不开 counter()counters() 函数。正如前面所演示的示例,counter-resetcounter-incrementcounter()counters()一起使用。这两个函数的定义如下:

<counter> = <counter()> | <counters()>
counter()  =  counter( <counter-name>, <counter-style>? )
counters() = counters( <counter-name>, <string>, <counter-style>? )

counter()counters()函数有两个相同的参数,即<counter-name><counter-style>,其中:

  • <counter-name>是计数器名称,它可以是隐式的list-item,也可以是counter-reset在元素上实例化的计数器名称,甚至是由conter-increment实例化的计数器名称
  • <counter-style>是计数器样式,也就是 CSS的list-style-type定义的计数器样式(预定义的计数器样式),也可以是 CSS Counter Style Level 3规范中 @counter-style 定义的计数器样式

counters()counter()函数多一个参数,即<string>,外侧和内侧计数器样式相连接的符号,比如.

counter()counters()两个函数具体的描述是:

  • counter() 返回一个代表计数器的当前值的字符串
  • counters() 是一个嵌套计数器,返回表示指定计数器当前值的连接字符串。counters()函数有两种形式,即 counters(name, string)countters(name, string, style)

来看一个简单地示例:

ol {
    counter-reset: order;
}

li {
    counter-increment: order;
}

ol:nth-child(1) li::before {
    content: counter(order);
}

ol:nth-child(2) li::before {
    content: counters(order, ".");
}

也可以改变<string><counter-style>的样式:

ol {
    counter-reset: order;
}

li {
    counter-increment: order;
}

ol:nth-child(1) li::before {
    content: counter(order, var(--style, decimal));
}

ol:nth-child(2) li::before {
    content: counters(order, "_", var(--style, decimal));
}

注意,<counter-style>只有数字(Number),字母顺序(Alphabetic)和 Fixed 几种类型适用。

有了counter()counters()以及counter-resetcounter-increment修复前面示例存在的问题了:

ul {
    counter-reset: order;
}

li {
    counter-increment: order;
}

li::after {
    content: counter(order, decimal-leading-zero);
}

使用计数器生成编号之后的效果:

CSS 计数器能做什么?

CSS计数器使用场景还是蛮多的,比如排行榜的效果:

也可以用于一些动效中,比如模拟电影播放前倒计时的效果:

还可以实现一些带有交互行为的效果,比如

使用 CSS 计数器显示当前步骤

使用CSS计数器实现打靶计分的小游戏的效果

使用 CSS 计数器实现评分系统效果

使用 CSS 计数器实现计数效果

除了上面演示的效果之外,还可以使用 CSS 计数器实现更多的效果。

小结

在这篇文章中我们主要围绕着 CSS 的列表项标记符样式展开,并且向大家阐述了,我们如何使用@counter-style实现自定义列表项样式标记符样式。 虽然在::marker中可以更进一步对标记符样式做设计,但还有是一定的局限性。也就是说,如果想设计更具个性化的标记符样式,还是离开不::before::after伪元素,因为在这两个伪元素上能用更多的 CSS 样式规则。除此之外,还和大家一起探讨了 CSS 计数器相关的属性,比如counter-resetcounter-incrementcounter-setcounter()counters()实现更多的效果,甚至带有交互的、游戏化的效果。

简单地说,计数器可以实现比简单列表进行编号更有用的东西(效果)。也许有一天,这些知识在解决你工作中的一些问题时就能派上用场。