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

CSS 计数器不是 JavaScript,但能真正在 DOM 渲染时生成序号,且不污染 HTML 结构。它适合目录、章节、FAQ 列表、嵌套标题等需要语义化编号的场景,但容易因 counter-reset 作用域或 counter-increment 触发时机出错。
什么时候必须用 counter-reset?
只有当你要「重置计数起点」时才需要它——比如每级 <h2></h2> 开始新序列,或每个 <section></section> 独立编号。不写 counter-reset,计数器会沿用上一个同名计数器的值,导致编号累加而非重开。
常见误用:counter-reset: section; 放在 body 上,结果所有 <h2></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>)上错误使用了 <code>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决定“谁来开启新序列”,不是“谁来开始计数”。实际项目中,最容易被忽略的是计数器名称的命名冲突和重置位置错配——尤其在组件化结构里,不同模块用相同计数器名却没隔离作用域,会导致全局编号错乱。











