20

Is it possible to create an ordered list like the following? I like this for a table of contents I'm creating.

  1. Into
  2. Section1
    2.1 SubSection1
    2.2 SubSection2
  3. Section2
    .....

I have the following but each subsection restarts from 1.

<ol>
    <li>
        <a href="#Lnk"></a>
    </li>
    <li>
        <a href="#Lnk"></a>
    </li>
    <ol>
        <li>
            <a href="#Lnk"></a>
        </li>
        <li>
            <a href="#Lnk"></a>
        </li>
    </ol>
</ol>

Thanks

5 Answers 5

18

This can indeed be done with pure CSS:

ol {
    counter-reset: item;
}

li {
    display: block;
}

li:before {
    content: counters(item, ".")" ";
    counter-increment: item;
}

Same example as a fiddle.

2
  • 1
    I couldn't get this to work. Copying the last <ol> tag gave a number 2.3 at the top level instead of the expected 3.
    – pheon
    Commented Apr 11, 2019 at 17:36
  • 2
    @pheon that fiddle has a slight typo. ol can only have li children. Any nested ol must be wrapped within an li. Updated: jsfiddle.net/jasonkarns/6xkzv37e
    – jasonkarns
    Commented Apr 26, 2019 at 20:48
4

There's quite a number of jQuery plugins to generate a table of contents.

1
1

Have you seen this post: Number nested ordered lists in HTML

I don't think it can be done without using JS.

2
1

This code leads to the desired output for me:

<ol>
  <li>
    <a href="#Lnk">foo</a>
  </li>
  <li>
    <a href="#Lnk">bar</a>
    <ol>
      <li>
        <a href="#Lnk">baz</a>
      </li>
      <li>
        <a href="#Lnk">qux</a>
      </li>
    </ol>
  </li>
  <li>
    <a href="#Lnk">alpha</a>
    <ol>
      <li>
        <a href="#Lnk">beta</a>
      </li>
      <li>
        <a href="#Lnk">gamma</a>
      </li>
    </ol>
  </li>
</ol>

CSS:

ol {
    counter-reset: item;
}
li {
    display: block;
}
li::before {
    content: counters(item, ".")". ";
    counter-increment: item;
}

Fiddle: http://jsfiddle.net/Lepetere/evm8wyj5/1/

0

For myself I was not happy with existing solutions. So I created a solution with Python3 and BeautifulSoup.

The function take HTML source as string and looks for header tags (e.g. h1). In the next steps an id= is created for the header and also corresponding toc entry.

def generate_toc(html_out):
    """Create a table of content based on the header tags.
    
    The header tags are used to create and link the toc.
    The toc as place on top of the html output.
    
    Args:
        html_out(string): A string containing the html source.

    Returns:
        (string): The new string.
    """
    from bs4 import BeautifulSoup

    # the parser
    soup = BeautifulSoup(html_out, 'html.parser')

    # create and place the div element containing the toc
    toc_container = soup.new_tag('div', id='toc_container')
    first_body_child = soup.body.find_all(recursive=False)[0]
    first_body_child.insert_before(toc_container)
    # toc headline
    t = soup.new_tag('p', attrs={'class': 'toc_title'})
    t.string = 'Inhalt'
    toc_container.append(t)

    def _sub_create_anchor(h_tag):
        """Create a toc entry based on a header-tag.
        The result is a li-tag containing an a-tag.
        """
        # Create anchor
        anchor = uuid.uuid4()
        h_tag.attrs['id'] = anchor  # anchor to headline
        # toc entry for that anchor
        a = soup.new_tag('a', href=f'#{anchor}')      
        a.string = h_tag.string
        # add to toc
        li = soup.new_tag('li')
        li.append(a)
        return li

    # main ul-tag for the first level of the toc
    ul_tag = soup.new_tag('ul', attrs={'class': 'toc_list'})
    toc_container.append(ul_tag)

    # helper variables
    curr_level = 1
    ul_parents = [ul_tag]

    # header tags to look for
    h_tags_to_find = [f'h{i}' for i in range(1, 7)]  # 'h1' - 'h6'
    for header in soup.find_all(h_tags_to_find):
        next_level = int(header.name[1:])

        if curr_level < next_level:  # going downstairs
            # create sub ul-tag
            sub_ul_tag = soup.new_tag('ul', attrs={'class': 'toc_list'})
            # connect it with parent ul-tag
            ul_parents[-1].append(sub_ul_tag)
            # remember the sub-ul-tag
            ul_parents.append(sub_ul_tag)
        elif curr_level > next_level:  # going upstairs
            # go back to parent ul-tag
            ul_parents = ul_parents[:-1]

        curr_level = next_level

        # toc-entry as li-a-tag
        li_tag = _sub_create_anchor(header)
        # add to last ul-tag
        ul_parents[-1].append(li_tag)

    return soup.prettify(formatter='html5')

This is maybe not elegant in all of your use cases. Myself I use to put TOC's on top of HTML reports generated by data sciences routines (e.g. pandas).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.