Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

In the code below, I need to use the div tag at the top of the HTML for styling. Without the div tag in place, the hx tags are outline numbered correctly, but with the div in place everything goes completely wrong. I need this to work like this, but with the div tag still in place, and I need it to work for divs with different ids. Any ideas?

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>
like image 698
Alby Avatar asked Jul 27 '15 16:07

Alby


Video Answer


1 Answers

The reason for the behavior can be explained in detail by having a look at what the W3C specs say about creation of counters, their scope and inheritance.

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.


Why does the snippet without div work?

In the working snippet (the one without the div), the following is what happens:

  • counter.h1 (added a prefix to differentiate from element) is created (or reset) at body and its initial value is set as 0.
  • All elements inherit their parent's counters and so every element within body gets counter.h1. When the first h1 is encountered, the value of counter.h1 is incremented to 1. When the next h1 is encountered, it inherits counter value from the previous element and then increments to 2.
  • counter.h2 counter is created at h1 element and value is set to 0. This value is visible to the siblings of the h1 and they can all inherit it.
  • In this snippet, all h2 elements are actually siblings of the h1 element and so each h2 element inherits counter.h2 that was already created at the h1 and just increments its value. So, when the first h2 is encountered counter.h2 becomes 1 and so on.
  • Similar to h2 elements, the h3 elements are also siblings of both the h1 and h2 elements and so they also inherit counter.h1 and counter.h2. This is why the numbering remains correct in this sample.

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>

Why does the snippet with div not work?

Now let us to come to the snippet that doesn't work (the one where the h1 is present within a div).

  • Here, the h1 creates counter.h2 but this can be inherited only by the siblings of the h1 (of which there are none).
  • Whenever a h2 element is encountered, UA tries to increment value of counter.h2 within the :before selector. But here the h2 parent does not inherit counter.h2 and hence h2:before doesn't either. Because of this h2:before will create its own counter.h2 and increment to 1.
  • Subsequent h2 elements also cannot inherit counter.h2 because the counter is created by h2:before (which is a child of h2). Because of this, each time a h2 is encountered a new counter is created within its :before and incremented. This is why all h2 show up as 1.1.
  • Similarly, none of the h3 elements know about counter.h2 and they don't increment it either and this is why they show up as 1.0.x.
  • However, they all can inherit counter.h3 because it was created by h2 element which is a sibling of all h3 elements. This is why counter.h3 gets incremented properly.

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>

What is the solution?

Ideal solution to this problem would be to reset all 3 counters first at body itself so that all elements are aware of the existence of a counter and can inherit or use its value.

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>
like image 100
Harry Avatar answered Sep 27 '22 21:09

Harry