浏览器对每个创建独立计数上下文,不继承父级编号,故子必从1或a重置;正确嵌套须将子置于父内,否则成并列列表;跨层级连续编号需css counter-reset与counter-increment实现。

为什么嵌套 总是从 1 或 A 重新开始
浏览器对每个 <ol></ol> 都创建独立计数上下文,不继承父级编号值。哪怕结构上是“第二章下的子列表”,只要它是一个新 <ol></ol> 标签,就会重置为 1. 或 A. ——这不是 bug,是 HTML 规范行为。
常见错误现象:写成两个并列 <ol></ol>,或把子 <ol></ol> 放在父 <ol></ol> 外部(比如直接跟在 后面),DOM 树里它就不是子项,而是兄弟节点,自然无法延续计数。
- ✅ 正确结构必须是:
<ol> <li>A.<ol><li>1.</li></ol> </li> <li>B.</li> </ol> - ❌ 错误结构示例:
<ol><li>A.</li></ol> <ol><li>1.</li></ol>(两个独立<ol></ol>) - 验证方式:用开发者工具检查子
<ol></ol>是否为父<li>的直接子节点
如何用 CSS counter 实现「1.1 → 1.1.1 → A」这类跨层级连续编号
type 属性只改显示符号,不控制计数逻辑;想拼接多级、混用数字/字母/罗马字,必须用 counter-reset 和 counter-increment 显式管理。
关键点在于:每级需独立命名计数器,避免覆盖;用 counters() 自动提取嵌套路径,比手动拼 counter(outer) + "." + counter(inner) 更可靠(后者在某层缺失时会出 1..2 这类空段)。
- 外层
<ol class="chapter"></ol>设counter-reset: chapter; - 中层
<ol class="section"></ol>设counter-reset: section;(不能复用chapter名) - 触发编号的
<li>上加counter-increment: chapter;或counter-increment: section; - ::before 内用
content: counters(chapter, ".") " ";渲染「1.1.1」,用counters(section, ".")则取当前路径下所有section计数器
display: none 的
不会触发 counter-increment。计数器递增依赖元素是否参与渲染流,display: none 的元素被完全排除在布局之外,浏览器跳过它的样式计算和计数逻辑。
但 visibility: hidden 不同——元素仍占文档流、仍参与计数,只是不可见。这点容易被忽略,尤其在动态显示/隐藏列表项时。
- 想跳过某项编号?删掉它的
counter-increment声明,别只靠隐藏 - 想让某项编号+2?写
counter-increment: section 2; - 计数器作用域严格绑定 DOM 位置:
counter-reset必须写在最外层容器(如<ol></ol>),counter-increment必须写在要编号的<li>上
IE11 及更早版本的兼容性底线在哪里
counter-reset、counter-increment、counters() 在 IE11 中部分失效(比如 counters() 不支持第二个参数,或嵌套层级错乱)。若目标环境含旧版企业内网系统,fallback 方案只能回到纯 type 属性嵌套。
这意味着放弃「1.2.1」这种格式,接受各层独立编号:外层 <ol type="1"></ol>,内层 <ol type="a"></ol>,再内层 <ol type="i"></ol> ——语义清晰、兼容性好,但无法跨层级连续。
- 移动端和现代桌面浏览器无需担心,
counters()支持已全覆盖 - 若用构建工具,可加 PostCSS 插件自动注入 fallback 样式,但无法修复逻辑缺陷
- 真正难处理的是「混合格式拼接」需求(如
2.A.iii),这在原生type下根本不可行,只能 CSS counter + JS 补位
<ol></ol> 没包在 <li> 里,或者用了太多 wrapper div 把语义层压扁了。计数器本身很稳定,但它只响应合法的嵌套树。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











