必须用 counter-reset 时是需重置计数起点,如每级 h2 开始新序列或每个 section 独立编号;不写则沿用同名计数器累加值;counter() 取单级值,counters() 支持嵌套编号(如“1.2.3”),仅用于 ::before/::after 的 content 中。

CSS 计数器不是 JavaScript,但能真正在 DOM 渲染时生成序号,且不污染 HTML 结构。它适合目录、章节、FAQ 列表、嵌套标题等需要语义化编号的场景,但容易因 counter-reset 作用域或 counter-increment 触发时机出错。
什么时候必须用 counter-reset?
只有当你要「重置计数起点」时才需要它——比如每级 <h2> 开始新序列,或每个 <section> 独立编号。不写 counter-reset,计数器会沿用上一个同名计数器的值,导致编号累加而非重开。
常见误用:counter-reset: section; 放在 body 上,结果所有 <h2> 共享一个计数;正确做法是把它放在容器元素上:
section {
counter-reset: section;
}
section h2 {
counter-increment: section;
content: counters(section, ".") " ";
}
counters() 和 counter() 的区别在哪?
counter() 只取当前层级值,counters() 支持嵌套计数(如 “1.2.3”)。它不是函数调用,而是 CSS 伪元素 content 中的特殊语法,只能用于 ::before / ::after。
立即学习“前端免费学习笔记(深入)”;
-
counter(section)→ 输出3 -
counters(section, ".")→ 输出1.2.3(需配合多层counter-reset+counter-increment) -
counters(section, "-")→ 输出1-2-3,第二个参数是分隔符,仅支持字符串字面量
注意:counters() 的嵌套依赖父元素是否也设置了 counter-increment,否则只显示最内层数字。
为什么 content 没生效?常见失效原因
这是最常卡住的地方:CSS 计数器本身不渲染任何内容,必须靠 content 配合伪元素输出。如果没看到编号,请逐项检查:
- 目标元素是否用了
::before或::after,且写了content属性 -
content值是否为counter(x)或counters(x, "..."),而不是字符串如"counter(x)" - 元素是否是
display: none或visibility: hidden(前者会跳过计数,后者仍计数但不可见) - 是否在非替换元素(如
<div>)上错误使用了content—— 它只对伪元素有效
示例修正:
h3::before {
counter-increment: subsection;
content: counter(subsection) ". "; /* 不是 content: "counter(subsection)" */
}嵌套编号时,counter-reset 必须写在父容器上
很多人试图在 h3 上同时写 counter-reset: subitem 和 counter-increment: subsection,结果子项编号全乱。正确逻辑是:父元素负责「重置子计数器」,子元素只负责「递增自己层级」。
比如实现「2.1」「2.2」「2.3」:
h2 {
counter-reset: subsection; /* 每个 h2 重置 subsection */
counter-increment: section;
}
h2::before {
content: counter(section) ". ";
}
h3 {
counter-increment: subsection;
}
h3::before {
content: counter(section) "." counter(subsection) " ";
}关键点:嵌套编号不是靠一个计数器完成的,而是多个命名计数器按层级协作;counter-reset 决定“谁来开启新序列”,不是“谁来开始计数”。
实际项目中,最容易被忽略的是计数器名称的命名冲突和重置位置错配——尤其在组件化结构里,不同模块用相同计数器名却没隔离作用域,会导致全局编号错乱。


















