告别手动编号,让 CSS 帮你自动管理多级标题序号
前言
在编写技术文档、学术论文、法律条款或者任何需要结构化编号的内容时,我们经常会遇到这样的需求:
第一章 基础知识
1.1 什么是CSS
1.2 计数器的原理
1.2.1 基本概念
1.2.2 使用方法
1.3 实际应用
第二章 高级技巧
2.1 嵌套计数器
2.2 自定义样式
在Word中实现自动级联编号是比较方便的,本博客中已就这个主题写过专文。最近遇到一个需求,要编写带编号的HTML文档,与Word文档类似,手动输入这些编号不仅繁琐,而且一旦需要在中间插入或删除章节,所有后续编号都要手动调整,简直是噩梦。幸好HTML中也可以利用 CSS计数器功能实现与Word类似的自动级联编号,本文将详细介绍相关方法。
一、核心概念:CSS 计数器
CSS 计数器本质上就是由 CSS 维护的变量 ,它可以在指定元素上递增,并将当前值输出到 ::before 或 ::after 伪元素中。
三个核心属性
| 属性 | 作用 | 示例 |
|---|---|---|
counter-reset |
创建并重置计数器, 并可以指定计数器的初始值 | counter-reset: section; counter-reset: section initial-value; |
counter-increment |
递增计数器 | counter-increment: section; |
counter() |
返回一个代表计数器的当前值的字符串。它通常和伪元素搭配使用,但是理论上可以在支持值的任何地方使用。 | content: counter(section); |
最简单的例子
css
/* 创建计数器 */
body {
counter-reset: section;
}
/* 每个 h2 递增并显示编号 */
h2 {
counter-increment: section;
}
h2::before {
content: counter(section) ". ";
}
html
<h2>第一章</h2> <!-- 显示 "1. 第一章" -->
<h2>第二章</h2> <!-- 显示 "2. 第二章" -->
二、单级编号:最基础的用法
场景1:文章段落编号
css
article {
counter-reset: paragraph;
}
p {
counter-increment: paragraph;
}
p::before {
content: "【" counter(paragraph) "】";
margin-right: 8px;
color: #c0392b;
}
场景2:自定义编号格式
counter() 的第二个参数可以指定数字样式:
css
/* 罗马数字:Ⅰ、Ⅱ、Ⅲ */
content: counter(section, upper-roman);
/* 小写罗马数字:ⅰ、ⅱ、ⅲ */
content: counter(section, lower-roman);
/* 字母:a、b、c */
content: counter(section, lower-alpha);
/* 大写字母:A、B、C */
content: counter(section, upper-alpha);
/* 中文数字:一、二、三 */
content: counter(section, cjk-ideographic);
三、两级编号:章节 + 小节
这是最常用的场景,比如文档的「章」和「节」。
HTML 结构
html
<div class="document">
<h2>基础概念</h2>
<h3>什么是计数器</h3>
<h3>基本用法</h3>
<h2>高级技巧</h2>
<h3>嵌套计数器</h3>
<h3>自定义样式</h3>
</div>
CSS 实现
css
.document {
counter-reset: chapter; /* 创建章计数器 */
}
h2 {
counter-increment: chapter; /* 每遇到 h2,章编号 +1 */
counter-reset: section; /* 每章开始时,重置节计数器 */
}
h2::before {
content: "第" counter(chapter, cjk-ideographic) "章 ";
}
h3 {
counter-increment: section; /* 每遇到 h3,节编号 +1 */
margin-left: 1.5em;
}
h3::before {
content: counter(chapter) "." counter(section) " ";
}
显示效果
第一章 基础概念
1.1 什么是计数器
1.2 基本用法
第二章 高级技巧
2.1 嵌套计数器
2.2 自定义样式
关键点
counter-reset 的位置很重要:
- 放在父容器:创建计数器
- 放在
h2上:每章重置节计数器
四、三级编号:章 + 节 + 小节
CSS 实现
css
.document {
counter-reset: chapter;
}
h2 {
counter-increment: chapter;
counter-reset: section; /* 重置节计数器 */
}
h2::before {
content: "第" counter(chapter) "章 ";
}
h3 {
counter-increment: section;
counter-reset: subsection; /* 重置小节计数器 */
margin-left: 1.5em;
}
h3::before {
content: counter(chapter) "." counter(section) " ";
}
h4 {
counter-increment: subsection;
margin-left: 3em;
}
h4::before {
content: counter(chapter) "." counter(section) "." counter(subsection) " ";
}
HTML 示例
html
<div class="document">
<h2>基础知识</h2>
<h3>CSS 基础</h3>
<h4>选择器</h4>
<h4>盒模型</h4>
<h3>计数器</h3>
<h4>基本概念</h4>
<h4>使用方法</h4>
<h2>高级技巧</h2>
<h3>嵌套计数器</h3>
<h4>原理讲解</h4>
<h4>代码示例</h4>
</div>
显示效果
第1章 基础知识
1.1 CSS 基础
1.1.1 选择器
1.1.2 盒模型
1.2 计数器
1.2.1 基本概念
1.2.2 使用方法
第2章 高级技巧
2.1 嵌套计数器
2.1.1 原理讲解
2.1.2 代码示例
五、CSS配置更简单的多级有序级联编号列表
使用<ol> 和 <li>创建列表结构,再结合 counters() 函数创建简化CSS的多级级联编号。
场景:带有详细说明的章节
html
<ol>
<li><span>概述</span>
<p>这一章介绍基本概念。</p>
<ol>
<li>定义
<p>计数器的定义...</p>
</li>
<li><div class="title">作用</div>
<ol>
<li>作用一
<p>维护计数器变量...</p>
</li>
<li>作用二
<p>提供供伪元素content树型显示的编号...</p>
<ol>
<li>作用二的下级标题
<p>详细说明...</p>
</li>
<li>再来一个下级标题
<p>再来一段详细说明...</p>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
<li>实现方法
<p>详细讲解实现步骤。</p>
<ol>
<li>基础用法
<p>最简单的计数器...</p>
</li>
<li>高级特性
<p>嵌套和级联...</p>
</li>
</ol>
</li>
</ol>
<hr />
<ol>
<li>另一个列表</li>
<li>完全重新开始编号</li>
</ol>
CSS 实现
css
<style>
ol {
counter-reset: section;
list-style-type: none;
}
li::before {
counter-increment: section;
content: counters(section, ".") " ";
}
p {
text-indent: 2em; /*相对父元素缩进两个字符*/
}
.title {
display: inline-block;
}
</style>
注意:
- 如果希望编号与标题文本排列在同一行,`li`的第一个元素应当是行内元素,例如`#Text`或`span`等默认行内显示的结点,如果是`div`或`p`之类块结点,应当使用`css`给其配置`display: inline-block`样式。
- counters() 函数是一个嵌套计数器,返回表示指定计数器当前值的连接字符串,它有两种形式:counters(name, string) 或 counters(name, string, style)。它通常和伪元素搭配使用,但是理论上可以在支持值的任何地方使用。
六、更复杂的编号格式
格式1:带括号的编号
css
h2::before {
content: "(" counter(chapter) ") ";
}
h3::before {
content: "(" counter(chapter) "." counter(section) ") ";
}
/* 显示:(1) 标题、(1.1) 小节 */
格式2:法律条款风格
css
.article {
counter-reset: clause;
}
.clause {
counter-increment: clause;
}
.clause::before {
content: "第" counter(clause) "条 ";
font-weight: bold;
}
.paragraph {
counter-increment: paragraph;
margin-left: 2em;
}
.paragraph::before {
content: "第" counter(clause) "." counter(paragraph) "款 ";
}
格式3:中英文混合
css
h2::before {
content: "Chapter " counter(chapter) ": ";
}
h3::before {
content: counter(chapter) "." counter(section) " ";
}
/* 显示:Chapter 1: 标题、1.1 小节 */
七、样式美化
基础美化
css
h2::before {
content: counter(chapter) ".";
background-color: #3498db;
color: white;
padding: 4px 10px;
border-radius: 20px;
margin-right: 12px;
font-size: 0.9em;
}
h3::before {
content: counter(chapter) "." counter(section);
background-color: #ecf0f1;
padding: 2px 8px;
border-radius: 15px;
margin-right: 10px;
font-size: 0.8em;
}
带图标的风格
css
h2::before {
content: "📖 " counter(chapter) ".";
color: #2c3e50;
}
h3::before {
content: "▸ " counter(chapter) "." counter(section);
color: #7f8c8d;
}
八、注意事项与常见问题
1. counter-reset 和 counter-increment 的执行顺序
当同一个元素同时包含这两个属性时,先重置,再递增:
css
li {
counter-reset: section; /* 1. 先重置为 0 */
counter-increment: chapter; /* 2. 再递增到 1 */
}
2. 伪元素必须设置 content
没有 content 属性,伪元素不会显示:
css
/* ❌ 错误:缺少 content */
h2::before {
counter-increment: section;
}
/* ✅ 正确 */
h2::before {
counter-increment: section;
content: counter(section);
}
3. 计数器名称的命名规则
- 可以使用字母、数字、下划线、连字符
- 不能以数字开头
- 区分大小写
css
/* ✅ 有效 */
counter-reset: section1;
counter-reset: chapter_01;
counter-reset: main-section;
/* ❌ 无效 */
counter-reset: 1section; /* 不能以数字开头 */
4. 多个计数器用空格分隔
css
/* 同时重置多个计数器 */
counter-reset: chapter section subsection;
/* 同时递增多个计数器 */
counter-increment: chapter section;
5. 浏览器兼容性
CSS 计数器支持所有现代浏览器,包括:
- Chrome 4+
- Firefox 2+
- Safari 3.1+
- Edge 12+
- IE 8+(部分支持)
可以放心在生产环境中使用。
九、CSS 计数器样式(@counter-style)配置详解
@counter-style 是 CSS 中一个功能强大的规则,它允许你完全自定义列表项、计数器或其他需要序列化数字或符号的标记样式。它超越了传统的 list-style-type,提供了对计数器符号、前缀、后缀、范围、回退机制等的精细控制。
基本语法结构
@counter-style 规则的基本语法如下:
css
@counter-style <counter-style-name> {
system: <counter-system>;
symbols: <counter-symbols>;
/* 其他描述符... */
}
<counter-style-name>: 你为这个自定义计数器样式定义的名称,之后可以在list-style-type、counter-increment、counter()/counters()函数中使用它。system: 定义计数器系统算法,是核心描述符。symbols: 定义计数器使用的符号序列,通常与system配合使用。- 其他描述符:用于定义前缀、后缀、范围、回退样式等。
核心描述符详解
1. system 描述符
system 决定了计数器如何根据其值生成表示形式。主要值有:
-
cyclic(循环) : 循环使用symbols中定义的符号列表。当计数器值超过符号数量时,回到列表开头重新开始。- 示例 :
symbols: '◉' '○' '◎';对于值 1, 2, 3, 4, 5 会显示为: ◉, ○, ◎, ◉, ○。
- 示例 :
-
numeric(数字) : 使用位值数字系统(如十进制、二进制)。symbols定义了从 0 开始的数字符号。- 示例 :
symbols: '0' '1'; system: numeric;会生成二进制计数器 (1, 10, 11, 100... 对应符号为 1, 10, 11, 100...)。这是默认的数字系统。
- 示例 :
-
alphabetic(字母) : 使用类似字母的计数系统(如 a, b, c,... aa, ab,...)。symbols定义了"数字",第一个符号代表 1。- 示例 :
symbols: 'a' 'b' 'c'; system: alphabetic;对于值 1, 2, 3, 4, 5, 6, 7 会显示为: a, b, c, aa, ab, ac, ba。
- 示例 :
-
symbolic(符号) : 类似alphabetic,但在每个循环后通过重复符号来增加长度(如 *, **, ***,...)。- 示例 :
symbols: '★' '☆'; system: symbolic;对于值 1, 2, 3, 4 会显示为: ★, ☆, ★★, ☆☆。
- 示例 :
-
fixed(固定) : 为有限个计数器值提供固定的符号列表。列表用完后,由fallback描述符指定的样式接管。- 示例 :
system: fixed; symbols: '①' '②' '③';只有前三个列表项使用这些符号,从第四项开始使用回退样式。
- 示例 :
-
extends(扩展): 继承一个已存在的计数器样式,并允许你覆盖其部分描述符。这比从头定义更简洁。- 示例 :
@counter-style circled-alpha { system: extends lower-alpha; suffix: ' '; }继承小写字母样式,只修改后缀。
- 示例 :
2. symbols 描述符
定义用于构建计数器表示的符号序列。可以是字符串、图片(url())或转义字符。其具体行为由 system 决定。
css
symbols: 'A' 'B' 'C'; /* 字符串 */
symbols: '\2022' '\25E6' '\25AA'; /* Unicode 转义字符 */
symbols: url(star.svg) url(diamond.svg); /* 图片(支持有限) */
3. prefix 和 suffix 描述符
定义在计数器表示前后添加的固定内容。
prefix: 前缀,如"第 "。suffix: 后缀,如" 章"或". "(常见的列表后缀)。
css
@counter-style my-chapter {
system: numeric;
symbols: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';
prefix: "第";
suffix: "章";
}
/* 输出:第1章、第2章... */
4. range 描述符
定义该计数器样式生效的计数器值范围。超出范围的值将使用 fallback 样式。
- 值可以是
auto(默认,基于系统推荐)或由infinite关键字和整数组成的列表。
css
range: 1 10; /* 仅在值 1 到 10 之间生效 */
range: infinite -5, 10 infinite; /* 在 (-∞, -5] 和 [10, +∞) 生效 */
5. fallback 描述符
当自定义样式无法表示当前计数器值(例如,值超出 range,或 fixed 系统列表耗尽)时,指定一个回退的计数器样式。
css
fallback: lower-roman; /* 回退到小写罗马数字 */
fallback: disc; /* 回退到实心圆点 */
6. negative 描述符
定义当计数器值为负数时,在数值前后添加的符号。
css
negative: "(-" ")"; /* 对于值 -3,显示为 (-3) */
7. pad 描述符
用于在计数器表示达到最小宽度时进行填充,常用于生成固定位数的编号(如 001, 002,...)。
pad接受两个参数:<最小长度> <填充符号>。
css
pad: 3 "0"; /* 最小显示3位,不足用0填充 */
/* 值 1 显示为 "001", 值 25 显示为 "025", 值 100 显示为 "100" */
8. speak-as 描述符
指示屏幕阅读器等语音合成器如何朗读该计数器。值有:
auto: 根据system自动判断。bullets: 朗读为无序列表(可能不读数字)。numbers: 朗读为数字。words: 朗读为单词(如 "one", "two")。spell-out: 逐个拼读符号(对于字母)。
css
speak-as: numbers; /* 确保即使符号是图形,也以数字朗读 */
综合示例
css
/* 定义一个自定义的"任务步骤"计数器 */
@counter-style task-step {
system: fixed; /* 固定前几个符号 */
symbols: '❶' '❷' '❸' '❹' '❺'; /* 前五项使用特殊符号 */
suffix: '. '; /* 后缀是点加空格 */
fallback: decimal; /* 第六项开始回退到普通十进制数字 */
range: 1 20; /* 只对1-20的值生效,超出范围也用回退 */
}
/* 应用样式 */
ol.steps {
list-style: task-step;
padding-left: 2em;
}
浏览器兼容性提示
@counter-style 是一个较新的 CSS 特性。虽然它在现代浏览器(如 Chrome 91+、Firefox 33+、Safari 17+、Edge 91+)中已得到良好支持,但在旧版浏览器或某些特定环境下可能无法工作。在生产环境中使用时,建议通过 @supports 规则进行特性检测,并提供可靠的备用方案(如标准的 decimal)。
css
@supports (system: cyclic) {
/* 支持 @counter-style 时的样式 */
ol { list-style: my-cool-counter; }
}
@supports not (system: cyclic) {
/* 不支持时的降级样式 */
ol { list-style: decimal; }
}
通过 @counter-style,你可以为文档创建高度品牌化、本地化或具有特定设计要求的编号系统,大大提升了 CSS 在内容呈现上的表达能力。
十、实战案例:复杂级联编号的完整文档大纲
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS @counter-style 四级级联编号示例</title>
<style>
/* 1. 定义核心计数器样式:将数字映射为中文数字 */
@counter-style chinese-numeric {
system: alpha; /* 使用数字系统 */
symbols: "一" "二" "三" "四" "五" "六" "七" "八" "九" "十"; /* 定义1-10的符号,超出会组合,如十一 */
suffix: ""; /* 符号后不加默认的'.' */
}
/* 2. 重置全局样式,并设置多级计数器 */
body {
font-family: "Microsoft YaHei", sans-serif;
line-height: 1.6;
padding: 2rem;
max-width: 1000px;
margin: 0 auto;
background-color: #f9f9f9;
}
/* 隐藏所有列表的默认标记 */
ol {
list-style: none;
padding-left: 0;
}
/* 3. 为每一级列表定义计数器,并设置递增规则 */
/* 第一级:章 */
ol.level1 {
counter-reset: chapter; /* 初始化名为 chapter 的计数器 */
}
ol.level1 > li {
counter-increment: chapter; /* 每个一级 li,chapter 计数器加1 */
font-size: 1.5em;
font-weight: bold;
margin-top: 1.5em;
border-bottom: 2px solid #4a6fa5;
padding-bottom: 0.3em;
}
/* 第二级:节 */
ol.level2 {
counter-reset: section; /* 每个二级列表初始化 section 计数器 */
margin-top: 0.5em;
margin-left: 1.5em;
}
ol.level2 > li {
counter-increment: section;
font-size: 1.2em;
font-weight: 600;
margin-top: 1em;
}
/* 第三级:小节 */
ol.level3 {
counter-reset: subsection; /* 每个三级列表初始化 subsection 计数器 */
margin-left: 2.5em;
}
ol.level3 > li {
counter-increment: subsection;
margin-top: 0.8em;
}
/* 第四级:点 */
ol.level4 {
counter-reset: point; /* 每个四级列表初始化 point 计数器 */
margin-left: 3.5em;
}
ol.level4 > li {
counter-increment: point;
margin-top: 0.5em;
}
/* 4. 使用 ::before 伪元素和 counters() 函数生成编号 */
/* 通用规则:在所有 li 前添加生成的内容 */
ol.level1 > li::before,
ol.level2 > li::before,
ol.level3 > li::before,
ol.level4 > li::before {
display: inline-block;
margin-right: 0.5em;
font-weight: inherit;
color: #2c3e50;
}
/* 第一级编号:第X章 */
ol.level1 > li::before {
content: "第" counter(chapter, chinese-numeric) "章 ";
/* counter(chapter, chinese-numeric): 获取 chapter 计数器的值,并用 chinese-numeric 样式显示 */
}
/* 第二级编号:第X章-第Y节 */
ol.level2 > li::before {
content: "第"counters(chapter, "-", chinese-numeric) "章 - 第" counter(section, chinese-numeric) "节 ";
/* counters(chapter, "-", chinese-numeric): 获取所有祖先的 chapter 计数器值,用"-"连接,并用指定样式显示 */
}
/* 第三级编号:第X章-第Y节-第Z小节 */
ol.level3 > li::before {
content: "第"counters(chapter, "-", chinese-numeric) "章 - 第" counters(section, "-", chinese-numeric) "节 - 第" counter(subsection, chinese-numeric) "小节";
}
/* 第四级编号:第X章-第Y节-第Z小节-第N点 */
ol.level4 > li::before {
content: counters(chapter, "-", chinese-numeric) " - " counters(section, "-", chinese-numeric) " - " counters(subsection, "-", chinese-numeric) " - 第 " counter(point, chinese-numeric) " 点";
}
/* 5. 内容区域样式 */
.content {
display: inline-block;
vertical-align: top;
/*为编号留出空间,这是假定编号最宽为20em,如果编号宽度超过20em时,标题会被挤到编号的下一行*/
width: calc(100% - 20em);
}
/* 响应式调整 */
@media (max-width: 600px) {
.content {
width: 100%;
display: block;
margin-top: 0.3em;
}
ol.level2, ol.level3, ol.level4 {
margin-left: 1em;
}
}
</style>
</head>
<body>
<h1>CSS @counter-style 四级级联编号示例</h1>
<p>本示例使用 <code>@counter-style</code> 定义中文数字,并通过 <code>counters()</code> 函数实现"第X章-第Y节-第Z小节-第N点"格式的自动编号。</p>
<ol class="level1">
<li><span class="content">引言与概述</span>
<ol class="level2">
<li><span class="content">研究背景</span>
<ol class="level3">
<li><span class="content">国内研究现状</span>
<ol class="level4">
<li><span class="content">主要学术观点</span></li>
<li><span class="content">代表性文献</span></li>
</ol>
</li>
<li><span class="content">国外研究现状</span></li>
</ol>
</li>
<li><span class="content">研究意义</span></li>
</ol>
</li>
<li><span class="content">核心理论与方法</span>
<ol class="level2">
<li><span class="content">基础理论</span>
<ol class="level3">
<li><span class="content">理论框架</span></li>
<li><span class="content">关键概念</span></li>
</ol>
</li>
<li><span class="content">研究方法</span>
<ol class="level3">
<li><span class="content">定性分析</span></li>
<li><span class="content">定量分析</span>
<ol class="level4">
<li><span class="content">数据收集</span></li>
<li><span class="content">统计分析</span></li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
<li><span class="content">案例分析与应用</span>
<ol class="level2">
<li><span class="content">案例一</span></li>
<li><span class="content">案例二</span></li>
</ol>
</li>
</ol>
<footer>
<p>说明:此效果依赖于CSS的 <code>@counter-style</code> 和 <code>counters()</code> 函数,请在Chrome 91+、Firefox 33+、Safari 17+ 等现代浏览器中查看。</p>
</footer>
</body>
</html>
页面显示效果:

十一、总结
核心要点
| 要点 | 说明 |
|---|---|
| 三个属性 | counter-reset(创建/重置)、counter-increment(递增)、counter()(读取) |
| 重置/创建计数器 | 在需要前置编号的元素的父元素中进行 |
| 编号显示 | 通过需要前置编号的元素的 ::before 或 ::after 伪元素的 content 属性显示 |
| 多级嵌套 | 每级标题递增自己的计数器,并重置下一级 |
| 计数器命名规则 | 区分大小写,不能以数字开头 |
记忆口诀
父级创建子递增,
多级重置后递增。
伪元素里写内容,
content不能少。
何时使用 CSS 计数器?
✅ 适合使用:
- 技术文档、API 手册
- 学术论文、毕业论文
- 法律条款、合同条文
- 多级目录大纲
- 任何需要自动编号的结构化内容
❌ 不适合使用:
- 只需显示一次的场景(手动写更快)
- 需要 JavaScript 交互控制编号的场景
写在最后
CSS 计数器是一个被低估的强大功能。掌握了它,就再也不用担心文档结构调整时需要手动重排编号了。