Search code examples
htmlcssmultiple-columnscss-counter

CSS counter does not increment for <h2>


I am crafting a multicolumn, CSS-numbered, TOC like this:

| 1. TOC Level-1    | 2. TOC Level-1    |
| 1.1 TOC Level-2   | 2.1 TOC Level-2   |
| 1.2 TOC Level-2   | 2.2 TOC Level-2   |
| 1.3 TOC Level-2   | 3. TOC Level-1    |
                    | 3.1 TOC Level-2   |

Since CSS-columns are uncontrollable, wrapping is done with JS and a floating 'column' class.

Here's the example I follow: http://www.2ality.com/2012/01/numbering-headingshtml.html

Here's the CSS I use:

body {} .column {
  border: 2px dotted grey;
  float: left;
  width: 40%;
}
.menu-item:hover {
  cursor: pointer;
  color: blue;
}
.sub-nav-content {
  counter-reset: toc1;
}
h1 {
  counter-reset: toc2;
}
h1:before {
  counter-increment: toc1;
  content: counter(toc1)". ";
}
h2:before {
  counter-increment: toc2;
  content: counter(toc1)"." counter(toc2)" ";
}
<div class="sub-nav">
  <div class="sub-nav-content">
    <div class="column">
      <a class="menu-item"><h1>TOC entry level1</h1></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
    </div>
    <div class=column>
      <a class="menu-item"><h1>TOC entry level1</h1></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
      <a class="menu-item"><h1>TOC entry level1</h1></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
      <a class="menu-item"><h2>TOC entry level2</h2></a>
    </div>
  </div>
</div>

Somehow I can't get the Level-2 counter to increment.


Solution

  • Solution:

    You can achieve this using CSS counters by tweaking your HTML and CSS just a bit. Firstly, put the a inside the h1 or h2 instead of the other way around. Secondly, increment the counter at the h1 or h2 level but use the :before of the a to display the numbering. Thus the number will also have the :hover (as it is now part of the a).

    Demo:

    body {} .column {
      border: 2px dotted grey;
      float: left;
      width: 40%;
    }
    .menu-item:hover {
      cursor: pointer;
      color: blue;
    }
    .sub-nav-content {
      counter-reset: toc1;
    }
    h1 {
      counter-reset: toc2;
    }
    h1 {
      counter-increment: toc1;
    }
    h1 a:before {
      content: counter(toc1)". ";
    }
    h2 {
      counter-increment: toc2;
    }
    h2 a:before {
      content: counter(toc1)"." counter(toc2)" ";
    }
    <div class="sub-nav">
      <div class="sub-nav-content">
        <div class="column">
          <h1><a class="menu-item">TOC entry level1</a></h1>
          <h2><a class="menu-item">TOC entry level2</a></h2>
          <h2><a class="menu-item">TOC entry level2</a></h2>
          <h2><a class="menu-item">TOC entry level2</a></h2>
        </div>
        <div class=column>
          <h1><a class="menu-item">TOC entry level1</a></h1>
          <h2><a class="menu-item">TOC entry level2</a></h2>
          <h2><a class="menu-item">TOC entry level2</a></h2>
          <h1><a class="menu-item">TOC entry level1</a></h1>
          <h2><a class="menu-item">TOC entry level2</a></h2>
          <h2><a class="menu-item">TOC entry level2</a></h2>
          <h2><a class="menu-item">TOC entry level2</a></h2>
        </div>
      </div>
    </div>


    Reason for Original Issue:

    The below was your structure (simplified). What you were doing is using counter-reset for toc2 at the h1 level but this h1 is nested below the a.

    <div class="sub-nav">
      <div class="sub-nav-content">
        <div class="column">
          <a class="menu-item"><h1>TOC entry level1</h1></a>
          <a class="menu-item"><h2>TOC entry level2</h2></a>
          <a class="menu-item"><h2>TOC entry level2</h2></a>
          <a class="menu-item"><h2>TOC entry level2</h2></a>
        </div>
      </div>
    </div>
    

    As per W3C Specs, the scoping and inheritance of CSS counters works as follows:

    • The current element gets all counters that its parent or a sibling has. (counter inheritance)
    • It inherits the value of the counter from its previous sibling (or) the parent. (value inheritance)

    Here, since the toc1 counter was reset at the .sub-nav-content level, both the .column elements get the counter and all the .menu-item elements and their pseudo-element also get it (from their own parent). When the counter-increment is used on h1:before, it increments the value of the counter that was got from its parent, so all elements right up to .sub-nav-content know the existance of the counter and its current value. Thus each h1 is able to increment it properly.

    But the toc2 is only reset at h1 level. This means that none among .sub-nav-content, .column or .menu-item know about the existence of this counter. Only the h1 and its children know it. So, when the h2 elements are encountered, the UA sees if they already have that toc2 counter or not and as they don't have it, a new toc2 counter is created at each h2 and they are incremented. Thus their value will always ever be only 1 and nothing else.