Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any good codes to generate TOC from HTML heading elements in JavaScript?

I'd like to generate TOC like below in JavaScript:

<ol>
  <li>Heading 1</li>
  <li>Heading 2
    <ol>
      <li>Heading 2-1</li>
      <li>Heading 2-2</li>
    </ol>
  </li>
  <li>Heading 3</li>
</ol>

And the HTML codes to generate the TOC above:

<section id="toc">
  <p>This will be replaced with generated TOC.
</section>

<article>
  <h1>Heading 1<h1>
  <p>Bla bla bla.</p>

  <h1>Heading 2<h1>
  <p>Bla bla bla.</p>

    <h2>Heading 2-1<h2>
    <p>Bla bla bla.</p>

    <h2>Heading 2-2<h2>
    <p>Bla bla bla.</p>

  <h1>Heading 3<h1>
  <p>Bla bla bla.</p>
</article>

I'm really stuck :( How do you write the code to generate the TOC? I prefer jQuery or pure JavaScript.

UPDATE

This was quite tough for me, but somehow I suppose I've done:

  $(function () {
    var assigned_level = 0,
        current_level = 0,
        id_number = 1,
        parent_node = "article",
        toc_html = '';

    $(parent_node + " *").each(function () {
      if (this.nodeName.length === 2 && this.nodeName.charAt(0) === "H") {
        $(this).attr("class", "heading");
      }
    });

    $(".heading").each( function () {
      current_level = this.nodeName.charAt(1);

      $(this).attr('id', "toc-" + id_number);

      // Close a list if a same level list follows.
      if (assigned_level !== current_level - 1) {
        toc_html += "</li>"
      }

      // Open parent lists if a child list follows.
      while (assigned_level < current_level) {
        toc_html += "<ol>";
        assigned_level += 1;
      }

      // Close child lists and the parent list if
      // the same level parent list follows.
      while (assigned_level > current_level) {
        toc_html += "</ol></li>";
        assigned_level -= 1;
      }

      toc_html += 
        '<li><a href="#' + this.id + '">' + $(this).html() + "</a>";
      id_number += 1;
    });

    // Close everything
    while (assigned_level > 0) {
      toc_html += "</li></ol>";
      assigned_level -= 1;
    }

    $("#toc").html(toc_html);
  });

I still don't understand what I've done :P Perhaps there's more sophisticated ways. Please point me out anything you've found.

Thanks.

like image 462
Japboy Avatar asked Jan 28 '26 16:01

Japboy


1 Answers

first of all, you need to close your h1,h2 tags =)

if you do $("h1, h2, h3, h4, h5, h6") you'll get the tags in the same order they appear on the document. So you could do a loop on that array and check the level. e.g: if the last tag was H1 and you find a H2, it means that you need to create a <ol>. On the other hand, if you have a H3 and the next one is an H2, it means that you need to close the <ol>.

The hardest part would be to close the remaining <ol> in the end.

Hope this helps.

like image 199
pleasedontbelong Avatar answered Jan 30 '26 06:01

pleasedontbelong



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!