Using CSS to auto-number nested sections
Asked Answered
R

2

10

Say I have an HTML or XML document like this:

<body>
   <section>
      <h1>This should be numbered 1</h1>
      <section>
        <h1>This should be numbered 1.1</h1>
        <p>blah</p>
      </section>
      <section>
        <h1>This should be numbered 1.2</h1>
        <p>blah</p>
      </section>
   </section>
   <section>
      <h1>This should be numbered 2</h1>
      <section>
        <h1>This should be numbered 2.1</h1>
        <p>blah</p>
      </section>
   </section>
</body>

This is merely an illustrative example; in general, there can be any number of child-sections within a section, and sections can be nested to any depth.

Is it possible to use CSS (counters and generated content) to generate the desired section-number at the start of each section-title?

The only examples I've seen where this sort of thing works is with nested lists, but there you can attach 'counter-reset' to OL and 'counter-increment' to LI. Here, it seems like you need 'section' to both reset for its child-sections, and increment wrt its parent section, and attaching both of those to one element-name doesn't work.

Ramon answered 27/7, 2015 at 21:30 Comment(1)
take a look at CSS developer.mozilla.org/en/search?q=counter . it is the typical use of that rule :) . then use selectors like > to filter elements/childsIrregular
A
14

You should use CSS counters in this case.

Update solution (better). Finally, a little more flexible approach would be resetting counter on the body initially instead of section:first-child and also on any immediate next sibling of the h1.

body,
section h1 + * {
    counter-reset: section 0;
}

Updated solution. Turned out that my original solution is not very good as pointed in comments. Here is revised correct version which should work properly with arbitrary number of nested or sibling sections.

section:first-child,
section h1 + section {
    counter-reset: section 0;
}
section h1:before {
    counter-increment: section;
    content: counters(section, ".") " ";
}
<section>
    <h1>This should be numbered 1</h1>
    <section>
        <h1>This should be numbered 1.1</h1>
        <p>blah</p>
    </section>
    <section>
        <h1>This should be numbered 1.2</h1>
        <p>blah</p>
    </section>
    <section>
        <h1>This should be numbered 1.3</h1>
        <section>
            <h1>This should be numbered 1.3.1</h1>
            <p>blah</p>
        </section>
        <section>
            <h1>This should be numbered 1.3.2</h1>
            <p>blah</p>
        </section>
    </section>
    <section>
        <h1>This should be numbered 1.4</h1>
        <p>blah</p>
    </section>
</section>
<section>
    <h1>This should be numbered 2</h1>
    <section>
        <h1>This should be numbered 2.1</h1>
        <p>blah</p>
    </section>
    <section>
        <h1>This should be numbered 2.2</h1>
        <p>blah</p>
    </section>
</section>

Original (buggy). There is tricky part in this case: counter should increment for every subsequent section. This can be achieved with section + section selector:

section {
    counter-reset: section;
}
section + section {
    counter-increment: section;
}
section h1:before {
    counter-increment: section;
    content: counters(section, ".") " ";
}
<section>
    <h1>This should be numbered 1</h1>
    <section>
        <h1>This should be numbered 1.1</h1>
        <p>blah</p>
    </section>
    <section>
        <h1>This should be numbered 1.2</h1>
        <p>blah</p>
    </section>
</section>
<section>
    <h1>This should be numbered 2</h1>
    <section>
        <h1>This should be numbered 2.1</h1>
        <p>blah</p>
    </section>
</section>
Adapa answered 27/7, 2015 at 21:37 Comment(8)
Although this works for the specific example, it's not a general solution. Specifically, if the body (or any section) contains more than 2 child-sections, the ones after the second will all have the same label as the second ('2' or 'n.2' or 'n.n.2', etc). I've edited the problem statement to clarify that the section-tree can be arbitrarily wide as well as arbitrarily deep.Ramon
Hm, let me check for this.Adapa
@MichaelDyck Check updated solution, that should work better.Adapa
Yes, better. I needed to make two adjustments: (1) Re the selector section:first-child, I gather it's meant to only select "section 1". However, if there's some frontmatter before "section 1", the selector won't select anything, which will throw off all the section-numbering. Instead, the selector body works better. (2) Re the selector section h1 + section, if a section (e.g. 1.3) has an intro paragraph before its first child-section (e.g. 1.3.1), then the selector fails, which again throws off the numbering. Instead, section h1 + * avoids this problem. ...Ramon
... I realize that neither of these cases showed up in the original example, so I don't fault you for that. And your answer got me most of the way to the solution, so you get the check mark. But if you make the above changes in your answer, that may help people with the same problem in the future.Ramon
@MichaelDyck Yes, you are right, I updated answer with more generic solution. Only it's better to use section h1 ~ section instead of *.Adapa
No, section h1 ~ section doesn't work, because it selects all sibling-sections after an h1, not just the first. So in the example, it generates 1.1 for (what should be) 1.2 and 1.3.Ramon
Oh, you are actually right, I didn't test it, my bad.Adapa
R
-3
li{
text-align: center;
}


<ol type="1">
  <li>this</li>
  <li>is</li>
  <li>a</li>
  <li>List</li>
</ol>  

thats not testet but should work

Repentant answered 27/7, 2015 at 21:41 Comment(2)
You appear to be answering a completely different question.Ramon
but you want a nuberic list, and thats the easyest method for doing that i thought, im sorry if it doesn't help you :/Repentant

© 2022 - 2024 — McMap. All rights reserved.