如何阻止 <div> 标签干扰计数器?

How do I stop <div> tags interfering with counters?

在下面的代码中,我需要使用 HTML 顶部的 div 标记来设置样式。如果没有 div 标签,hx 标签的大纲编号是正确的,但是如果 div 就位,一切都会出错。我需要 this to work like this,但 div 标签仍​​然存在,我需要它来处理具有不同 ID 的 div。有什么想法吗?

body {counter-reset: h1}
h1 {counter-reset: h2}
h2 {counter-reset: h3}

h1:before {counter-increment: h1; content: counter(h1) ". "}
h2:before {counter-increment: h2; content: counter(h1) "." counter(h2) ". "}
h3:before {counter-increment: h3; content: counter(h1) "." counter(h2) "." counter(h3) ". "}
<div class="varies">
    <h1>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h1>
</div>
<p>Morbi efficitur nibh metus, a vehicula mauris tristique ac. Duis ornare metus eget iaculis hendrerit.</p>
  <h2>Morbi nisi lacus, ultricies sit amet turpis a, aliquet congue nulla.</h2>
        <p>Duis eget facilisis nisl.</p>
    <h3>Donec tincidunt purus quam, ut accumsan lorem hendrerit a.</h3>
      <p>Aenean in mattis quam.</p>
    <h3>Maecenas a nulla sit amet ligula facilisis tincidunt lacinia non enim.</h3>
      <p>Aliquam dignissim turpis placerat, facilisis magna et, venenatis purus.</p>
  <h2>Suspendisse tempus eu elit nec malesuada.</h2>
        <p>In ut sollicitudin nisi. Praesent non porttitor ante, molestie scelerisque mauris.</p>
  <h2>Vivamus eu turpis efficitur, ornare risus in, consectetur tellus.</h2>
        <p>Cras pellentesque orci eu placerat mollis.</p>      
<h1>Duis eu nulla et tellus porttitor auctor.</h1>

考虑一下(与你的结构非常相似,只是更简单一点):

body {
  counter-reset: h1;
}

h1:before { 
  content: counter(h1) ". ";
  counter-increment: h1;
}
h1 {
  counter-reset: h2;
}

h2:before {
  content: counter(h1) "." counter(h2) ". ";
  counter-increment: h2;
}
<div>
  <h1>Heading first level 1</h1>
</div>
  <h2>Heading second level 1</h2>
  <h2>Heading second level 2</h2>
  <h2>Heading second level 3</h2>
  <h2>Heading second level 4</h2>
  <h2>Heading second level 5</h2>    
<h1>Heading first level 2</h1>
  <h2>Test</h2>
  <h2>Test</h2>
  <h2>Test</h2>
<h1>Heading first level 3</h1>
<h1>Heading first level 4</h1>

为什么您的布局不起作用

根据推荐:

Counters are "self-nesting", in the sense that resetting a counter in a descendant element or pseudo-element automatically creates a new instance of the counter. This is important for situations like lists in HTML, where elements can be nested inside themselves to arbitrary depth.

您的布局中的问题是 <div> 中的第一个 <h1> 初始化了 h1 计数器的一个单独实例。发生这种情况是因为计数器对嵌套元素很敏感。每次重置不同级别的计数器时,都不会影响作用域为另一级别的同一个计数器实例!

会发生什么

情况如下:

01. <body>                             | h1 = *
02. <div>                              |
03.   <h1>Heading first level 1</h1>   | h1 = 1 |  div-h2 = *
04. </div>                             |
05.   <h2>Heading second level 1</h2>  |        | h2 = 1
06.   <h2>Heading second level 2</h2>  |        | h2 = 1
07.   <h2>Heading second level 3</h2>  |        | h2 = 1
08.   <h2>Heading second level 4</h2>  |        | h2 = 1
09.   <h2>Heading second level 5</h2>  |        | h2 = 1
10. <h1>Heading first level 2</h1>     | h1 = 2 | h2 = *
11.   <h2>Test</h2>                    |        | h2 = 1
12.   <h2>Test</h2>                    |        | h2 = 2
13.   <h2>Test</h2>                    |        | h2 = 3
14. <h1>Heading first level 3</h1>     | h1 = 3
15. <h1>Heading first level 4</h1>     | h1 = 4

如您所见,在 01. 中我们重置了计数器 h1,它工作正常。然而,在 h1 个元素中,我们重置了计数器 h2

问题出现在 03. 中,我们在嵌套级别 div 中重置了计数器 h2,为了澄清这一点,我将此计数器称为:div-h2。事实上,从 05.09. 的下 h2 个元素正在使用一个不同的计数器, 还没有被重置 !那是计数器 h2(无嵌套),即使他们试图增加它,也没有任何东西可以增加,因为重置是强制性的!

10. 中我们没有 div 嵌套,因此计数器 h2 被正确重置,因此也增加了。

做什么

对于您的情况,您必须避免在页面中生成不均匀的嵌套结构。这是一个干净的 HTML 的建议,在你的情况下,如果你真的需要保留那个 div,只需在重置计数器 h2 的地方添加一个 div 选择器:

div {
  counter-reset: h2;
}

所以这是最后的工作片段:

body {
  counter-reset: h1;
}

h1:before { 
  content: counter(h1) ". ";
  counter-increment: h1;
}
h1 {
  counter-reset: h2;
}

div {
  counter-reset: h2;
}

h2:before {
  content: counter(h1) "." counter(h2) ". ";
  counter-increment: h2;
}
<div>
  <h1>Heading first level 1</h1>
</div>
  <h2>Heading second level 1</h2>
  <h2>Heading second level 2</h2>
  <h2>Heading second level 3</h2>
  <h2>Heading second level 4</h2>
  <h2>Heading second level 5</h2>    
<h1>Heading first level 2</h1>
  <h2>Test</h2>
  <h2>Test</h2>
  <h2>Test</h2>
<div>
  <h1>Heading first level 1</h1>
</div>
  <h2>Test</h2>
  <h2>Test</h2>
  <h2>Test</h2>
<h1>Heading first level 3</h1>
<h1>Heading first level 4</h1>

如果您只有那些 div 嵌套 h1,则上述解决方案有效h3专柜也有!但我不推荐这样做,因为它是一个非常混乱的 CSS 安排:难以维护且不易遵循。

记住在CSS总有办法实现的事情,你说你需要那个div,但我认为你没有尝试所有的可能性来摆脱它。

可以通过查看 W3C specs 关于计数器的创建、它们的范围和继承的内容来详细解释该行为的原因。

Counter Reset: The counter-reset property creates new counters on an element.

Scope of a Counter: The scope of a counter starts at the first element in the document that has a 'counter-reset' for that counter.

Counter Inheritance: A counter and its value are inherited separately, possibly from different elements. If an element has a previous sibling, it must inherit all of the sibling’s counters. Otherwise, if the element has a parent, it must inherit all of the parent’s counters. Otherwise, the element must have an empty set of counters. The element then inherits counter values from the immediately preceding element in document order.


为什么没有 div 的代码段有效?

在工作代码段(没有 div 的代码段)中,发生了以下情况:

  • counter.h1(添加前缀以区别于元素)在body处创建(或重置),其初始值设置为0。
  • 所有元素都继承其父元素的计数器,因此 body 中的每个元素都得到 counter.h1。当遇到第一个h1时,counter.h1的值递增为1。当遇到下一个h1时,它继承前一个元素的计数器值,然后递增为2。
  • counter.h2 计数器在 h1 元素处创建,值设置为 0。此值对 h1 的兄弟姐妹可见,并且他们都可以继承它。
  • 在此代码段中,所有 h2 元素实际上都是 h1 元素的同级元素,因此每个 h2 元素继承已在 h1 并且只是增加它的值。所以,当遇到第一个h2时,counter.h2变成1,依此类推。
  • h2 元素类似,h3 元素也是 h1h2 元素的同级元素,因此它们也继承了 counter.h1counter.h2。这就是此示例中编号保持正确的原因。

body {counter-reset: h1}
h1 {counter-reset: h2}
h2 {counter-reset: h3}
h1:before {counter-increment: h1; content: counter(h1)". "}
h2:before {counter-increment: h2; content: counter(h1)"." counter(h2)". "}
h3:before {counter-increment: h3; content: counter(h1)"." counter(h2)"." counter(h3)". "}
<!-- body creates counter.h1 and set to 0 -->
<h1>Heading 1 <!-- Inherits counter.h1 from parent, creates counter.h2 and set to 0 -->
  <!-- ::before being a child inherits all counters from parent, increments counter.h1 to 1 and displays value -->
</h1>
<p>Paragraph</p>
<h2>Heading 2 <!-- Inherits counter.h1, counter.h2 from sibling, creates counter.h3 and set to 0 -->
  <!-- ::before being a child inherits all counters from parent, increments counter.h2 to 1 and displays value -->
</h2>
<p>Paragraph</p>
<h3>Heading 3 <!-- Inherits counter.h1, counter.h2, counter.h3 -->
  <!-- ::before being a child inherits all counters from parent, increments counter.h3 to 1 and displays value -->
</h3>
<p>Paragraph</p>
<h3>2nd Heading 3 <!-- Inherits counter.h1, counter.h2, counter.h3 -->
  <!-- ::before being a child inherits all counters from parent, increments counter.h3 to 2 and displays value -->  
</h3>
<p>Paragraph</p>
<h2>2nd Heading 2 <!-- Inherits counter.h1, counter.h2, counter.h3, resets counter.h3 to 0 -->
  <!-- ::before being a child inherits all counters from parent, increments counter.h2 to 2 and displays value -->
</h2>
<p>Paragraph</p>
<h2>3rd Heading 2 <!-- Inherits counter.h1, counter.h2, counter.h3, resets counter.h3 to 0 -->
  <!-- ::before being a child inherits all counters from parent, increments counter.h2 to 3 and displays value -->
</h2>
<p>Paragraph</p>
<h1>2nd Heading 1 <!-- Inherits counter.h1, counter.h2, counter.h3, resets counter.h2 to 0 -->
  <!-- ::before being a child inherits all counters from parent, increments counter.h1 to 2 and displays value -->
</h1>


为什么带有 div 的代码段不起作用?

现在让我们来看看不起作用的代码段(h1 出现在 div 中的代码段)。

  • 在这里,h1 创建了 counter.h2,但这只能由 h1 的兄弟姐妹继承(其中有 none)。
  • 每当遇到 h2 元素时,UA 都会尝试在 :before 选择器中增加 counter.h2 的值。但是这里 h2 父级不继承 counter.h2 因此 h2:before 也没有。因此 h2:before 将创建自己的 counter.h2 并递增到 1。
  • 后续的 h2 元素也不能继承 counter.h2,因为计数器是由 h2:before(它是 h2 的子元素)创建的。因此,每次遇到 h2 时,都会在其 :before 中创建一个新计数器并递增。这就是为什么所有 h2 都显示为 1.1 的原因。
  • 类似地,h3 元素中的 none 知道 counter.h2 并且它们也不会递增它,这就是它们显示为 1 的原因。 0.x
  • 但是,它们都可以继承 counter.h3,因为它是由 h2 元素创建的,该元素是所有 h3 元素的兄弟。这就是 counter.h3 正确递增的原因。

body {counter-reset: h1}
h1 {counter-reset: h2}
h2 {counter-reset: h3}
h1:before {counter-increment: h1; content: counter(h1)". "}
h2:before {counter-increment: h2; content: counter(h1)"." counter(h2)". "}
h3:before {counter-increment: h3; content: counter(h1)"." counter(h2)"." counter(h3)". "}
<!-- body creates counter.h1, sets it to 0 -->
<div class="test"> <!-- Inherits counter.h1 from parent -->
  <h1>Heading 1 <!-- Again inherits counter.h1 from parent, creates counter.h2 -->
    <!-- ::before increments counter.h1 to 1 and display value-->
  </h1>
</div>
<p>Paragraph</p>
<h2>Heading 2 <!-- Inherits counter.h1 as it is from parent but not counter.h2, creates counter.h3 -->
  <!-- ::before has no counter.h2, so creates a counter.h2 and increments to 1 -->
</h2>
<p>Paragraph</p>
<h3>Heading 3 <!-- Inherits counter.h1 as it is from parent, couunter.h3 from sibling but not counter.h2 -->
  <!-- ::before inherits counter.h3 from parent and increments to 1, has no counter.h2 so creates a new counter.h2 and sets to 0 -->
</h3>
<p>Paragraph</p>
<h3>2nd Heading 3 <!-- Inherits counter.h1 as it is from parent, couunter.h3 from sibling but not counter.h2 -->
  <!-- ::before inherits counter.h3 from parent and increments to 2, has no counter.h2 so creates a new counter.h2 and sets to 0 -->
</h3>
<p>Paragraph</p>
<h2>2nd Heading 2 <!-- Inherits counter.h1 as it is from parent, couunter.h3 from sibling but not counter.h2, resets counter.h3 to 0 -->
  <!-- ::before has no counter.h2, so creates a counter.h2 and increments to 1 -->
</h2>
<p>Paragraph</p>
<h2>3rd Heading 2 <!-- Inherits counter.h1 as it is from parent, couunter.h3 from sibling but not counter.h2, resets counter.h3 to 0 -->
  <!-- ::before has no counter.h2, so creates a counter.h2 and increments to 1 -->
</h2>
<p>Paragraph</p>
<h1>2nd Heading 1 <!-- Inherits counter.h1 as it is from parent, couunter.h3 from sibling but not counter.h2, resets counter.h2 to 0 -->
  <!-- ::before inherits counter.h1 from parent and increments to 2 -->
</h1>


解决方法是什么?

这个问题的理想解决方案是首先在 body 本身重置所有 3 个计数器,以便所有元素都知道计数器的存在并可以继承或使用它的值。

body {counter-reset: h1 h2 h3}
h1 {counter-reset: h2 h3}
h2 {counter-reset: h3}
h1:before {counter-increment: h1; content: counter(h1)". "}
h2:before {counter-increment: h2; content: counter(h1)"." counter(h2)". "}
h3:before {counter-increment: h3; content: counter(h1)"." counter(h2)"." counter(h3)". "}
<!-- body creates counter.h1, counter.h2, counter.h3 sets all 0 -->
<div class="test"> <!-- Inherits all 3 counters -->
  <h1>Heading 1 <!-- Inherits all 3 counters, resets counter.h2 and counter.h3 to 0 -->
    <!-- ::before also inherits all 3 counters, increments counter.h1 to 1 and displays value -->
  </h1>
</div>
<p>Paragraph</p>
<h2>Heading 2 <!-- Inherits all 3 counters, resets counter.h3 to 0 -->
  <!-- ::before also inherits all 3 counters, increments counter.h2 to 1 and displays value -->
</h2>
<p>Paragraph</p>
<h3>Heading 3 <!-- Inherits all 3 counters -->
  <!-- ::before also inherits all 3 counters, increments counter.h3 to 1 and displays value -->
</h3>
<p>Paragraph</p>
<h3>2nd Heading 3 <!-- Inherits all 3 counters -->
  <!-- ::before also inherits all 3 counters, increments counter.h3 to 2 and displays value -->
</h3>
<p>Paragraph</p>
<h2>2nd Heading 2 <!-- Inherits all 3 counters, resets counter.h3 to 0 -->
  <!-- ::before also inherits all 3 counters, increments counter.h2 to 2, resets counter.h3 to 0 and displays value -->
</h2>
<p>Paragraph</p>
<h2>3rd Heading 2 <!-- Inherits all 3 counters, resets counter.h3 to 0 -->
  <!-- ::before also inherits all 3 counters, increments counter.h2 to 3, resets counter.h3 to 0 and displays value -->
</h2>
<p>Paragraph</p>
<h1>2nd Heading 1 <!-- Inherits all 3 counters, resets counter.h2 and counter.h3 to 0 -->
  <!-- ::before also inherits all 3 counters, increments counter.h1 to 2, resets counter.h2, counter.h3 to 0 and displays value -->
</h1>