CSS counter-reset异常:多全局计数器在div处重置是预期行为还是Bug?
嘿,这个情况其实是CSS计数器规范里的细节导致的预期行为,不是bug哦!咱们一步步来捋清楚原因,再给你靠谱的解决方案:
一、为啥同时开两个计数器就会在div处重置?
当你在body上同时写多个counter-reset(比如counter-reset: figure-counter table-counter;)时,浏览器会把这俩计数器的初始化上下文绑定到body的渲染块里。而div是块级容器,会创建新的格式化上下文,这时候浏览器的计数器处理逻辑就会出个小状况:如果父元素(body)上有多个计数器被重置,子块级元素(div)会默认“继承”这个重置规则,但实际上会触发计数器重新初始化——结果就是每个div开头,计数器都被打回原形了。
但如果只开一个计数器,浏览器的处理逻辑就简单多了,不会触发这个上下文重置的问题,自然能从开头数到页面末尾。
针对你问的两个补充点:
为啥是第一个div就重置,不是等到body末尾?
因为body是所有div的父元素,div作为直接子块级元素,渲染的时候会优先读取父元素的计数器重置规则。当父元素有多个计数器时,浏览器会在每个新的格式化上下文(也就是每个div)开始时重新计算计数器状态,不会等到body结束才收尾。怎么让两个计数器各自独立跑完全程?
你不需要搞嵌套层级,只要让计数器的初始化上下文脱离body的块级上下文,或者明确告诉浏览器别让子元素干扰计数器就行。
二、具体解决办法
方案1:把计数器重置移到html上(最推荐)
把counter-reset放到html元素而不是body上,这样计数器的初始化上下文是整个文档的根元素,body下的div就没法干扰它了:
html { counter-reset: figure-counter table-counter; } figure.figure { counter-increment: figure-counter; } figure.figure figcaption b::before { content: "Figure " counter(figure-counter) ": "; } /* 对应的table计数器样式,按需添加 */ table.table { counter-increment: table-counter; } table.table caption b::before { content: "Table " counter(table-counter) ": "; }
不管以后加多少个计数器,这个方法都能保证它们各自独立计数,不会被div打断。
方案2:给计数器指定初始值
如果不想碰html的样式,也可以在body上重置的时候给每个计数器明确写初始值(哪怕是0),告诉浏览器别在子元素里乱重置:
body { counter-reset: figure-counter 0 table-counter 0; } /* 后面的增量和内容样式保持不变 */
方案3:空div占位(备选,不推荐)
你提到的加空div放计数器的方法也能用,但确实有点冗余,比如在body开头加个空div专门负责重置:
<div style="counter-reset: figure-counter table-counter;"></div> <!-- 后面的div内容原样保留 -->
这种方法能解决问题,但多了个无用的DOM元素,不如前两种优雅。
总结
核心问题就是多计数器在body块级上下文中的作用域冲突,把重置移到html元素是最简洁靠谱的办法,不管以后加多少计数器都能hold住。
内容的提问来源于stack exchange,提问作者otf-it

