用CSS计数器实现多级标题自动编号:body设counter-reset: h1,各级标题用counter-increment递增并配合::before生成编号,注意层级重置、缩进对齐及打印兼容性。

如何用 CSS 实现自动编号的多级 <h1>–<h6> 标题
浏览器原生不支持标题自动编号,必须靠 CSS 计数器(counter-reset / counter-increment)配合 ::before 伪元素实现。别试图用 JS 动态插入数字——维护成本高、SEO 不友好、打印时易失效。
关键点:
-
<h1>触发顶层计数器重置,<h2>–<h6>按层级嵌套递增对应计数器 - 每个层级需独立声明
counter-increment,例如h2 { counter-increment: h2; } - 编号格式用
counter()函数拼接,如counter(h1) "." counter(h2),不能写成counter(h1.h2) - 若文档中存在跳级(如
<h2>后直接<h4>),缺失层级的计数器不会自动补零,需手动控制或接受空号
为什么 counter-reset: h1 放在 body 而不是 h1 上
把 counter-reset: h1 写在 h1 元素自身样式里,会导致每次 <h1> 都重置自己——编号全变成 1。正确做法是让最外层容器(通常是 body)初始化顶层计数器,后续标题只负责递增。
示例片段:
立即学习“前端免费学习笔记(深入)”;
body {
counter-reset: h1;
}
h1 {
counter-increment: h1;
}
h1::before {
content: counter(h1) ". ";
}
h2 {
counter-increment: h2;
}
h2::before {
content: counter(h1) "." counter(h2) ". ";
}
注意:h2 的 counter-reset 必须放在 h1::before 或 h1 的样式里(比如 h1 { counter-reset: h2; }),否则 h2 会持续累加而非按章节归零。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
处理标题缩进与编号对齐的实际问题
编号后文字默认紧贴,但多级标题需要视觉层级感。单纯用 margin-left 容易破坏语义结构,推荐用 text-indent + padding-left 组合:
-
h2及以下用text-indent: -2em把编号拉回行首,再用padding-left: 2em留出空白,确保文字左对齐 - 避免对
::before设置宽度或display: inline-block,否则换行时编号可能错位 - 若用
flex布局控制标题,::before会脱离文本流,编号与文字间距不可控,不建议
常见错误:content: "§" counter(h1) 这类带符号的写法,若符号字体缺失或字号不一致,会导致编号和符号间空隙忽大忽小——统一用空格或点分隔更稳。
兼容性与打印场景下的坑
所有现代浏览器都支持 CSS 计数器,但 IE8 及更早版本完全不支持。如果必须兼容 IE8,只能退回到 JS 注入或服务端生成编号。
打印 PDF 时(如 Chrome “另存为 PDF”):
- 部分旧版 wkhtmltopdf 对
counter()渲染不稳定,建议升级到 0.13.0+ 版本 - 若标题含
display: flex或grid,::before可能被忽略,打印前临时切回block - 不要依赖
@media print里重写计数逻辑——编号状态不会重置,会导致页眉页脚编号错乱
最易被忽略的是:当页面通过 iframe 加载或使用 Shadow DOM 时,counter-reset 作用域受限,需在对应上下文内单独声明。


















