4

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.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Marc Smits
  • 53
  • 6
  • No they wouldn't...CSS Counter are **highly** structure dependent. When you wrap the second 'column' you re-start the count. – Paulie_D Jun 19 '16 at 09:14
  • Thanks @Paulie_D. Whats the best way to achive what I need? – Marc Smits Jun 19 '16 at 10:11
  • Something like this? http://output.jsbin.com/gakolo – Mosh Feu Jun 19 '16 at 11:01
  • Thank you @Mo for investigating – Marc Smits Jun 19 '16 at 11:20
  • @MarcSmits: There is no need to change your wrap structure. Just change the inner structure and put the `h1`, `h2` under the `a`. That should do it. – Harry Jun 19 '16 at 11:20
  • Thank you @MoshFeu for investigating. Like Pauli_D has also stated: **Any form of wrapping an element, stops a CSS-counter to properly increment*. Selecting a element with CSS (including the number) requires it to be wrapped. The only way to achieve the above, is to hard-code the numbering, which means backend will have to generate it. – Marc Smits Jun 19 '16 at 11:27
  • @MarcSmits: No, it doesn't. I guess you didn't read my comment. Your structure only need minor modification and CSS counter WILL work. – Harry Jun 19 '16 at 11:32
  • @Harry that could work, when only the `h1` `h2` would have a hover-state, neglecting the numbering `:before`. – Marc Smits Jun 19 '16 at 11:32
  • No @MarcSmits You can still make the `:before` have the hover effect. I can post an answer if you wish. – Harry Jun 19 '16 at 11:32
  • I worked it out!!! Thanks @Harry. https://jsfiddle.net/ctrlspatie/141awch6/2/ – Marc Smits Jun 19 '16 at 11:45
  • @MarcSmits: But you see that the number is *continuing* under 2nd and 3rd `h1` right? It is not 1, 2, 3. It is starting from where it left off in the previous one. Is that what you needed? I don't think so. Anyway, I've also posted an answer with a working solution and also the reason why your original code didn't work. – Harry Jun 19 '16 at 11:47

1 Answers1

3

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.

Harry
  • 87,580
  • 25
  • 202
  • 214