用counter-reset初始化计数器、counter-increment递增、::before+counter()显示序号;多组独立编号需不同计数器名或在新父容器重置;序号不显因content缺失、元素脱离文档流或计数器名大小写不一致。

如何用 counter-increment 和 ::before 给列表项加自动序号
直接给元素加序号,不靠 HTML 的 <ol></ol>,也不手动写 1、2、3——核心就两步:先用 counter-reset 初始化计数器,再用 counter-increment 在每个目标元素上触发递增,最后用 ::before + counter() 把数字“吐”出来。
常见错误是只写了 counter-increment 却忘了在父容器设 counter-reset,结果所有序号都是 0 或 NaN;或者把 counter() 写在了伪元素的 content 外面,根本不出值。
-
counter-reset一般放在父容器(比如<div class="list">)上,值是计数器名,如 <code>counter-reset: item; -
counter-increment放在要编号的子元素上,如.list-item { counter-increment: item; } -
::before { content: counter(item) ". "; }是唯一能显示数字的地方,content属性不可省略 - 计数器名是作用域敏感的,不同父容器里的同名计数器彼此隔离
- 不要试图用
counter-reset: item 0;在每个子项上“重置回 0”,这会导致全部变成 1 - 如果某组需要跳过编号(比如标题行),给它加
counter-increment: none;,避免误增 -
counter(item)输出纯数字,如5 -
counter(item, lower-roman)可加样式,支持decimal、lower-alpha、upper-roman等,但不支持自定义字符映射 -
counters()第二个参数是分隔符,必须用引号包裹,如" > ",否则解析失败 -
::before或::after必须声明content,哪怕只是content: "";,空值或缺失都会让整个伪元素不渲染 - 计数器名区分大小写,
my-counter和My-Counter是两个计数器 - 动态插入的 DOM(如 JS append)如果没触发重排,可能不会立刻执行
counter-increment,需要强制触发一次样式读取(如getComputedStyle(el).color)
多个独立编号区域怎么互不干扰
一个页面里有好几组“第 X 条说明”“第 Y 步操作”,它们得各自从 1 开始——关键不是用同一个计数器名,而是为每组起不同名字,或在同一名字下用 counter-reset 重置。
比如两组步骤:step-a 和 step-b,分别设置 counter-reset: step-a; counter-reset: step-b;;更常见的做法是复用名字但重置位置:第二组容器开头再写一遍 counter-reset: step;,它就会覆盖之前的状态,新序列从 1 起跳。
counter() 和 counters() 的区别和选法
counter() 用于单层编号,counters() 用于嵌套结构(比如 1.1、1.2、2.1)。实际项目中 90% 场景用不到 counters(),但一旦用了又没配对 counter-reset,就会出现 “1.1.1.1” 这种无限嵌套的错乱。
比如二级菜单想显示 “2-3” 这种格式,得在父级设 counter-reset: section;,子级设 counter-reset: subsection;,然后子项用 counters(section, "-") ——注意中间的连接符必须是字符串字面量,不能是变量或 CSS 变量。
为什么序号不显示?检查这三处硬性限制
CSS 计数器不是“渲染即生效”的特性,它依赖几个隐性前提:伪元素必须有 content、目标元素必须参与文档流、计数器名大小写必须完全一致。任何一处断链,序号就消失,连报错都不会有。
典型静默失败场景:父容器用了 display: flex 或 display: grid,子项又设置了 position: absolute,导致子项脱离文档流,counter-increment 就不触发;或者把 counter-reset: Item; 和 counter-increment: item; 混用大小写,浏览器直接忽略后者。











