css计数器适用于嵌套多级编号、跨元素连续计数和条件性编号等ol无法实现的场景;当编号逻辑脱离dom层级(如跨容器、隐藏元素不中断计数、带前缀嵌套)时必须用counter-increment。

CSS Counters 不能替代语义化 HTML 列表,但能解决「嵌套多级编号」「跨元素连续计数」「条件性编号」这类 <ol></ol> 做不到的场景。
什么时候必须用 counter-increment 而不是 <ol></ol>
当编号逻辑脱离 DOM 层级结构时,<ol></ol> 就会失效。比如:多个不相邻的 <section></section> 需共享同一计数器;某个 <div class="note"> 要插入到编号流中;或需要「1.1、1.2、2.1」这种带前缀的嵌套格式,且子项可能被 CSS <code>display: none 隐藏但又不想中断计数。
-
<ol></ol>的编号由元素顺序和start属性控制,无法跨容器、无法跳过、无法重置为非数字值 -
counter-increment是声明式计数,只要匹配到选择器就触发,与是否渲染、是否父子关系无关 - 典型用例:
h2自动编号 +h3继承上级编号(如「2.1」「2.2」),哪怕中间夹着<blockquote></blockquote>或<figure></figure>
counter-reset 和 counter-increment 的作用差异
counter-reset 定义计数器名称并初始化值(默认为 0),counter-increment 才真正让计数器加 1(或指定步长)。两者必须配合使用,且 counter-reset 必须写在「作用域起始元素」上(通常是父容器或标题本身)。
- 错误写法:
body { counter-increment: section; }—— 没有counter-reset,计数器未声明,无效 - 正确写法:
article { counter-reset: section; }+h2 { counter-increment: section; } - 支持步长:
counter-increment: section 2;表示每次加 2;负数也合法:-1 - 可同时操作多个计数器:
counter-increment: section subsection;
用 counter() 和 counters() 输出编号
counter(name) 输出单级计数器值;counters(name, separator) 用于嵌套,自动拼接父级值(如 counters(section, ".") 在 h3 里输出「1.1」、「1.2」)。
-
counter(section)只取当前层级值,适合平级列表 -
counters(section, ".")依赖父级的counter-reset是否存在 —— 如果父h2有counter-reset: subsection,而你对h3写counters(subsection, "-"),它不会回溯到section,必须名字一致 - 前缀/后缀用伪元素内容拼接:
h2::before { content: counter(section) ". "; } - 避免在
content中直接写字符串加号:content: "第" counter(section) "节"是合法的,但counter(section) + "节"会报错(CSS 不支持运算符拼接)
容易被忽略的继承与作用域陷阱
CSS 计数器不是全局变量,而是基于「格式化上下文」和「样式继承链」工作的。最常踩的坑是:子元素没继承到父级 counter-reset,导致 counters() 输出为空或 0。
-
counter-reset默认不继承,但它的效果会影响后代中所有匹配counter-increment的元素 - 如果父容器用了
display: flex或display: grid,而子元素是position: absolute,它仍属于该格式化上下文,计数器有效 - 但若中间有
contain: layout或transform创建新层叠上下文(某些浏览器),可能截断计数器传播 —— 这类边界情况需实测 - 调试技巧:临时加
h2::after { content: " [" counter(section) "]"; color: red; }看值是否递增











