CSS counters 深度介绍

CSS counters 深度介绍学过一天前端的小白都知道 html 里面有一个标签叫做 ol orderlist 通过 ol 和 li 的嵌套 我们能够得到前面有数字标号的列表 这位小白如果再多学几天 也许还会知道 css 里有一个属性叫做 list style type 通过它 能够控制列表标号的类型 从大小圆点 到数字 大小写英文 罗马字母不等 不过 这位小白也许再学很久都不会接触到 csscounter 这是一个古老

学过一天前端的小白都知道,html 里面有一个标签叫做 ol——order list。通过 ol 和 li 的嵌套,我们能够得到前面有数字标号的列表。这位小白如果再多学几天,也许还会知道,css 里有一个属性叫做 list-style-type。通过它,能够控制列表标号的类型,从大小圆点、到数字、大小写英文、罗马字母不等。

不过,这位小白也许再学很久都不会接触到 css counter。这是一个古老但实用的属性,给予我们更灵活更强大地控制列表标号的能力。下面我们就来介绍它。

css counters 的属性

  • counter-reset
  • counter-increment
  • counter() / counters()

依次说明:

1.counter-reset。

明译为计数器重置。形如:counter-reset: level1

其中,level1 只是示例,实际上是可以任意命名的一个名字标识符。

按我的理解,counter-reset 的真实意思是:在目标元素所在的层级中定义一个计数器。

2.counter-increment

明译为计数器累加。形如: counter-increment: level1 1

其中,level1 是通过 counter-reset 定义的计数器名,这里的 1 也可以是任意其他整数,甚至可以是负数。当浏览器渲染页面时,带这个属性的元素每出现一次,当前层级内对应名字的计数器就增加相应的值。如不写,默认是 1。

3.counter() / counters()

前面两个属性定义了计数器和计算规则,不过,这些计数器的计算目前都是在内存中进行的。counter() 和 counters() 就负责把计数器显示出来。这两个计算方法要和伪元素的 content 属性搭配食用。形如:content: counter(level1)。counter 计算符前后可以随意加字符串来对最后的效果做拼接。

*注意:当 couner() 一个没有定义过的计数器时,会显示 1。

下面来看一个最简单的例子。

例1:

html:

<div class="level1"> <div class="level1-item">foo 
       div> <div class="level1-item">bar 
      div>  
      div>复制代码

css:

.level1 { counter-reset: level1; } .level1-item { counter-increment: level1 1; /* counter-increment写到伪类的选择器中也是可以的 */ } .level1-item:before { content: 'step' counter(level1) '.'; }复制代码

效果:

很简单是吧。至此,想必读者对 css counter 的属性的基本用法已经有所了解了。下面开始进阶了,请系好安全带。

进阶应用

使用 list-style-type

counter() 和 counters() 方法内还可以应用 list-style-type。只要把 list-style-type 属性的合法值写在计数器名后即可,中间用逗号分隔。

例2 (其余 css 和 html 保持一致):

.level1-item:before { content: counter(level1, upper-roman) '.'; }复制代码

效果:

重新开始计数

在同一层级中,可以通过重复声明相同名字的计数器来打断(或者说是覆盖)之前的计数,重新开始。

例3:

 
   
"level1">
"level1-item">foo
"level1-item">bar
"level1-item break">baz
"level1-item">qux
复制代码

.level1 { counter-reset: level1; } .break { counter-reset: level1; } .level1-item:before { content: counter(level1, upper-roman) '.'; counter-increment: level1 1; }复制代码

效果:

多层嵌套计数

重点来了。到上面为止,所有的效果我们都可以用普通的 ol 标签来实现,但下面的多层嵌套计数则是 css counter 独有的绝技,光用 ol 是实现不了的。(你要手动硬写标号那当我什么都没说)而这也是 css counter 最主要的用武之地。

例4:

<div class="level1"> <div class="level1-item"> china <div class="level1"> <div class="level1-item">newbee 
      div> <div class="level1-item"> lgd <div class="level1"> <div class="level1-item">lgd 
     div> <div class="level1-item">lfy 
     div>  
     div>  
     div>  
     div>  
     div> <div class="level1-item"> world <div class="level1"> <div class="level1-item">liquid 
     div>  
     div>  
     div>  
     div>复制代码

.level1 { counter-reset: level1; } .level1 div { padding-left: 10px; } .break { counter-reset: level1; } .level1-item:before { counter-increment: level1; content: counters(level1, '-') '. ' }复制代码

效果:

在上面的例子里,我们首次使用了 counters() 函数,它的第一个参数是计数器名字,第二个参数是一个连接符。counters() 函数会在文档中遍历查找指定计数器,并按照嵌套关系,用连接符把不同层级上的同名计数器的值连接起来,形成标号。

成是成了,不过我认为上面这个方法并不是一个好的实践,原因有二:

第一,列表中明明出现了三个层级,但我们编码时却一直在使用 level1 这一个计数器,让人困惑。

第二,我们引入了无意义的 html 标签

 。比如,newbee,lgd 都是中国战队,我们希望直接放到 china 的后面,而不是用一个额外的不具备什么语义的 level1 标签来把他们包裹起来。读者也可以发现,目前的 html 结构还是比较复杂的。

使用多个计数器的多层嵌套计数

仔细思考一下,我们发现,上面的问题都是因为我们只使用了一个计数器。考虑到 content 属性里可以使用多个 counter() 函数,我们完全可以把嵌套层级拆成多个计数器。

改造上面的例子。

例5:

<div class="level1"> <div class="level1-item"> china <div class="level2-item">newbee 
      div> <div class="level2-item"> lgd <div class="level3-item">lgd 
     div> <div class="level3-item">lfy 
     div>  
     div>  
     div> <div class="level1-item"> world <div class="level2-item">liquid 
     div>  
     div>  
     div>复制代码

.level1 { counter-reset: level1; } .level1-item:before { counter-reset: level2; counter-increment: level1; content: counter(level1) '.'; } .level2-item:before { counter-reset: level3; counter-increment: level2; content: counter(level1) '-' counter(level2) '.'; } .level3-item:before { counter-increment: level3; content: counter(level1) '-' counter(level2) '-' counter(level3)'.'; }复制代码

效果:

完美,和之前的一模一样。使用这种实现方式,html 的层级减少了,而通过引入了 level2 和 level3,整体的结构变得更加清晰了。

可是有的同学又要说了,不行,我还是更喜欢之前那种单个计数器的方式,一个 counters() 全搞定,酷炫。而且虽然 html 比现在多,但是 css 少呀!

嗯,说的有点道理,不过当遇到下面这种需求时,恐怕你只有使用多个计数器了。

结合嵌套计数器与自定义 list-style-tyle

上面的嵌套列表有一个特点,即每个层级的标号使用的都是数字。但实际需求可能更加灵活,比如第一级用一个大写字母,第二级用第一级标号+数字,第三级用小写罗马数字等。这种情况下,用一个 counters() 是实现不了的。

例6:

.level1 { counter-reset: level1; } .level1-item:before { counter-reset: level2; counter-increment: level1; content: counter(level1, upper-alpha) '.'; } .level2-item:before { counter-reset: level3; counter-increment: level2; content: counter(level1, upper-alpha) '-' counter(level2) '.'; } .level3-item:before { counter-increment: level3; content: counter(level3, lower-roman)'.'; }复制代码

效果:

完美实现!

从 counters() 的标号错乱来深入理解 css counters 

回过头看例4,如果我们把 html 的结构改成这样:

<div class="level1"> <div class="level1-item">国内战队 
      div> <div class="level1"> <div class="level1-item">newbee 
     div> <div class="level1-item">lgd 
     div> <div class="level1"> <div class="level1-item">lgd 
     div> <div class="level1-item">lfy 
     div>  
     div> <div class="level1-item">vg 
     div>  
     div> <div class="level1-item">国外战队 
     div> <div class="level1"> <div class="level1-item">liquid 
     div>  
     div>  
     div>  
     div>复制代码

css 保持不变,结果就变成了:

好吧,我们看到,原本标号应该是 1-3 的项变成了1-2-3,原本应该是 2 的项变成了 1-3,这是怎么回事儿呢?

分析当前结构和正确结构的区别,我们发现,最大的区别在于当前结构把概念上属于子级的 level1 容器写成了列表上一级的兄弟元素。还别说,如果不注意,我也很容易就写成这样了。

但为啥这么写就不对了呢?

按我的理解,计数器拥有一个活动范围,这个范围并不是计数器所在的元素本身,而是这个计数器所在的元素的父元素。换句话说,如果在一个元素上定义了计数器,那么这个元素的所有兄弟元素都能访问到这个计数器,而且将优先访问这个计数器。

优先级:

在元素自身上定义的计数器 > 在元素兄弟元素上定义的计数器 > 在元素父元素上定义的计数器

另外,元素无法访问到其兄弟元素的子元素定义的计数器。

在上面的例子中,紧跟在 level-1 后的兄弟元素 level-1-item 也能访问到 level-1 定义的计数器,而这个计数器在逻辑上已经是深层级的了。所以标号原本应该是 1-3 的项实际上读取的是它前面那个兄弟元素 level-1 定义的计数器,这个计数器实际上是第三层级,于是在原来的基础上顺着 +1,成了 1-2-3。

到头来,为什么一定要用 css counters ?

有些拼命拆我台的同学又要说了,css counters 太麻烦了,我还是不想用它。就算出现了嵌套列表的标号的需求,我直接用纯文本,把类似 A-1 这样的文字写进标签中不就行了?

行,如果你真的这么干了,我敬你是条汉子。但你有没有想过,如果在列表中增加了一条或者删除了一条,当前层级之后的所有列表项的标号,可都要变了。最极端的情况:如果产品说要把列表项的第一项删掉···?

嗯,教你两个选择。第一,打死产品。第二,打一开始就使用 css counters。

最后,来看一下兼容性

css counters 系列属性 css2 就加了,所以兼容性特别好,一片原谅色,大家放心大胆使用吧。

转载于:https://juejin.im/post/59df5acf6fb9a0d31f

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/226247.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 上午7:31
下一篇 2026年3月17日 上午7:31


相关推荐

  • 数据结构JAVA—递归算法「建议收藏」

    数据结构JAVA—递归算法「建议收藏」http://blog.csdn.net/wangjinyu501/article/details/8248492  原版一、基本概念       递归算法是一种直接或者间接调用自身函数或者方法的算法。Java递归算法是基于Java语言实现的递归算法。递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。递归算法对解决

    2022年7月8日
    20
  • msyql多个or,and,

    msyql多个or,and,

    2022年3月3日
    45
  • 车用总线技术 | J1939协议实用指南与J1939数据记录方案

    车用总线技术 | J1939协议实用指南与J1939数据记录方案“没错,这是一份SAEJ1939协议的简单、实用指南。”—虹科开篇:在这篇介绍中,我们介绍了J1939协议的基本知识,其中包括PGN和SPN。因为这是一篇偏向应用的简介,所以您还将会学习到如何通过DBC文件解码J1939数据、如何记录J1939、典型的应用案例和实用技巧。下面,来了解下这份简单易懂的J1939介绍吧~什么是J1939?J1939简介简而言之,SAEJ1939其实是一套标准,重型车辆ECU间就是按照这套标准在CAN总线上进行通信的。当今大多数车辆都通过CAN(Con…

    2022年5月1日
    654
  • GBDT算法详解_gbdt算法

    GBDT算法详解_gbdt算法基本思想GBDT的基本结构是决策树组成的森林,学习方式是梯度提升。具体的讲,GBDT作为集成模型,预测的方式是把所有子树的结果加起来。GBDT通过逐一生成决策子树的方式生成整个森林,生成新子树的过程是利用样本标签值与当前树林预测值之间的残差,构建新的子树。例如,当前已经生成了3课子树了,则当前的预测值为D(x)=d1(x)+d2()x+d3(x),此时我们得到的当前的预测值为D(x)效果并不好,与真正的拟合函数f(x)还有一定的差距。GBDT希望的是构建第四棵子树,使当前树林的预测结果D(x)与第四棵

    2022年10月12日
    5
  • h3c路由器配置命令大全_h3c命令手册

    h3c路由器配置命令大全_h3c命令手册
    H3C  相关  
     
    1、system-view  进入系统视图模式
    2、sysname  为设备命名
    3、displaycurrent-configuration当前配置情况
    4、language-modeChinese|English中英文切换
    5、interfaceEthernet1/0/1进入以太网端口视图
    6、portlink-typeAccess|Trunk|Hybrid     

    2022年10月18日
    5
  • 第八话-工厂方法模式

    第八话-工厂方法模式

    2022年1月18日
    77

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号