css计数器是实现“第1章”类自定义序号的最优解,需在ol设counter-reset、li设counter-increment、li::before用content插入;手动硬编码序号破坏语义且难维护。

ol标签的type属性只能控制数字/字母类型,不能自定义文字
直接用ol的type或start属性没法实现“第1章”“第2章”这种带文字前缀的序号——它只支持1、A、a、I、i五种内置类型。真要加“第X章”,得绕开原生序号逻辑。
用CSS counter-reset + counter-increment模拟自定义序号
这是最稳妥的方案,兼容性好(IE8+),且不破坏语义结构。核心是重置计数器、递增、再用::before插入内容。
常见错误现象:counter-increment写在li上但没给ol设counter-reset,结果序号全为0;或者用content: "第" counter(mycount) "章"却忘了counter-reset: mycount;。
实操建议:
-
ol元素上加counter-reset: chapter; -
li上加counter-increment: chapter; -
li::before里用content: "第" counter(chapter) "章"; - 如果想从第3章开始,把
counter-reset: chapter 2;(注意:这里值是起始前一个数)
ol {
counter-reset: chapter;
}
ol li {
counter-increment: chapter;
}
ol li::before {
content: "第" counter(chapter) "章 ";
font-weight: bold;
}
用data-*属性 + JavaScript动态生成(适合需要交互或复杂逻辑时)
当序号依赖后端数据、需响应式更新、或要支持“附录A-1”这类嵌套格式时,CSS计数器会变复杂。这时用data-chapter存原始值更可控。
使用场景:
- 章节编号来自API返回的
chapter_id字段 - 用户点击按钮切换“显示/隐藏序号”
- 导出PDF时需保证序号为静态文本(避免CSS未生效)
关键点:
- 别直接改
li.textContent,会删掉子内容;用li.insertBefore(span, li.firstChild) - 用
parseInt(li.dataset.chapter, 10)确保转成数字,避免字符串拼接出"第12章"变成"第12章"(其实是"第1"+"2章") - 若列表可能动态增删,把初始化逻辑包进函数,方便重跑
为什么不用list-style-type: none + 手动写序号?
有人图省事,在每个li里硬编码<span>第1章</span>内容。这看似简单,但会带来三个实际问题:
- 维护成本高:新增一章就得手动改所有后续序号
- 无法用CSS统一控制序号样式(比如批量改成“壹、贰、叁”)
- 屏幕阅读器可能把序号和内容一起读,失去列表语义(
ol本意是传达顺序关系)
真正需要“完全自由排版”的场景(比如序号斜体+背景色+图标),优先用CSS计数器,而不是放弃ol语义去手写。
最易被忽略的是counter-reset的作用域——它只影响后代元素,如果ol里嵌了div再包li,计数器就断了。保持li是ol的直接子元素,是最省心的做法。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











