CSS计数器(counter-reset和counter-increment)是实现自定义序号的核心方案,通过在ol设counter-reset、li设counter-increment、li::before用content插入,可灵活控制起始值、跳号、多级嵌套(如1.1)及可点击编号,彻底替代原生ol的样式限制。

用 counter-reset 和 counter-increment 控制序号生成
HTML 原生 <ol></ol> 的序号是只读的,不能直接在 <li> 里写数字或用 value 属性“覆盖”显示——value 只影响计数逻辑,不改变样式渲染,尤其在嵌套或 CSS 重置后容易失效。真要自定义外观(比如加前缀、换字体、插入图标),得靠 CSS 计数器。
核心思路:重置计数器 → 每个 <li> 自增 → 用 ::before 插入内容。
-
counter-reset: my-counter 0放在<ol></ol>上,初始化计数器(第二个参数是起始值) -
counter-increment: my-counter放在<li>上,每次渲染都加 1 -
content: counter(my-counter)写在<li>::before</li>里,把数值“吐出来”
给序号加前缀或后缀(比如 “Step 1.”、“→ 2”)
单纯显示数字太干,多数场景需要修饰。这时别拼接字符串,用 counter() 的第二个参数控制格式,再配合 content 拼接文本:
ol.custom {
counter-reset: step-counter;
}
ol.custom li {
counter-increment: step-counter;
}
ol.custom li::before {
content: "Step " counter(step-counter) ". ";
font-weight: bold;
}
注意:counter(step-counter, decimal) 中的 decimal 是默认值,可省略;换成 lower-roman 或 upper-alpha 就能切格式,但不能混用阿拉伯数字和中文数字(CSS 不支持 simp-chinese 这类)。
立即学习“前端免费学习笔记(深入)”;
value 属性为什么经常不生效?
<li value="5"> 确实存在,但它只重置后续项的计数起点,不强制当前项显示该值,更不会影响 CSS 计数器。常见翻车点:
- 父级
<ol></ol>用了list-style: none,value彻底不可见 - 嵌套
<ol></ol>里用value,外层计数器未重置,序号错乱 - 用 JS 动态增删
<li>后,value不自动更新,必须手动同步
结论:需要稳定可控的序号表现时,绕过 value,全程走 CSS 计数器。
多级列表里避免计数器互相干扰
嵌套 <ol></ol> 时,如果所有 <li> 都用同一个计数器名,子列表会接着父列表继续计数。正确做法是分层命名:
ol.level-1 { counter-reset: level1; }
ol.level-1 li { counter-increment: level1; }
ol.level-1 li::before { content: counter(level1) ". "; }
ol.level-2 { counter-reset: level2; }
ol.level-2 li { counter-increment: level2; }
ol.level-2 li::before { content: counter(level1) "." counter(level2) " "; }
这里子项用 counter(level1) 拿父级值,实现 “1.1”、“1.2” 效果。关键不是“嵌套自动继承”,而是显式调用上层计数器 —— 容易漏掉这一步,结果子项全从 1 开始。
复杂点在于层级动态变化时,计数器名得跟 HTML class 或 data 属性对齐,否则 JS 注入新节点后样式断掉。最稳的方式是放弃深度嵌套,改用扁平结构 + CSS 伪类模拟层级感。



















