Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a JavaScript solution to generating a "table of contents" for a page?

Tags:

javascript

I have headers in <h1> through <h6> tags. Is there a way that I can use JavaScript to generate a table of contents for the contents that serves as anchor tags as well?

I would like the output to be something like:

<ol>
    <li>Header 1</li>
    <li>Header 1</li>
        <li>Header 2</li>
            <li>Header 3</li>
</ol>

I am not currently using a JavaScript framework, but I don't see why I couldn't use one.

I am also looking for something done, since I'm guessing this is a common problem, but if not, a starting point to roll my own would be good.

like image 627
Thomas Owens Avatar asked Oct 09 '08 14:10

Thomas Owens


People also ask

How do you make a table of contents in HTML?

The HTML source code for the TOC (table of contents) will be inside a <template> tag. The code inside <template> doesn't get rendered until it's added to the document by JavaScript. Our TOC will have placeholders, held in <slot> tags, for all the headings and subheadings found in the document.

How do I generate a table of contents using jQuery?

Here is the code that generates the table of contents: This code iterates through all h2 and h3 elements using the jQuery call $ ("h2, h3").each (...) . For each h2 or h3 element, an <a name="index"></a> is inserted in the HTML document just before it.

How to create a table of contents using a container?

Just add the code below to your scripts and call TableOfContents (container, output); on load, where container is the class or id of your content element and output is the class or id of the TOC element. Default values are '#contents' and '#toc', respectively.

How do I make a table of contents contain <H2> elements?

You just want the table of contents to contain the <h2>, <h3> elements in the page. Here is an example of an HTML page like the one mentioned above: Notice the div with the id tocDiv, and the ul inside with the id tocList .

How do I add a table of contents to a Div?

The first thing we do is to get the <div>element that we are going to use for the table of contents from its ID, “Toc”. We then create a header, an <h2>, give it the text “Table of Contents”and append it to the ToC <div>.


4 Answers

I couldn't resist putting together a quick implementation.

Add the following script anywhere on your page:

window.onload = function () {     var toc = "";     var level = 0;      document.getElementById("contents").innerHTML =         document.getElementById("contents").innerHTML.replace(             /<h([\d])>([^<]+)<\/h([\d])>/gi,             function (str, openLevel, titleText, closeLevel) {                 if (openLevel != closeLevel) {                     return str;                 }                  if (openLevel > level) {                     toc += (new Array(openLevel - level + 1)).join("<ul>");                 } else if (openLevel < level) {                     toc += (new Array(level - openLevel + 1)).join("</ul>");                 }                  level = parseInt(openLevel);                  var anchor = titleText.replace(/ /g, "_");                 toc += "<li><a href=\"#" + anchor + "\">" + titleText                     + "</a></li>";                  return "<h" + openLevel + "><a name=\"" + anchor + "\">"                     + titleText + "</a></h" + closeLevel + ">";             }         );      if (level) {         toc += (new Array(level + 1)).join("</ul>");     }      document.getElementById("toc").innerHTML += toc; }; 

Your page should be structured something like this:

<body>     <div id="toc">         <h3>Table of Contents</h3>     </div>     <hr/>     <div id="contents">         <h1>Fruits</h1>         <h2>Red Fruits</h2>         <h3>Apple</h3>         <h3>Raspberry</h3>         <h2>Orange Fruits</h2>         <h3>Orange</h3>         <h3>Tangerine</h3>         <h1>Vegetables</h1>         <h2>Vegetables Which Are Actually Fruits</h2>         <h3>Tomato</h3>         <h3>Eggplant</h3>     </div> </body> 

You can see it in action at https://codepen.io/scheinercc/pen/KEowRK (old link: http://magnetiq.com/exports/toc.htm (Works in IE, FF, Safari, Opera))

like image 102
Ates Goral Avatar answered Oct 05 '22 07:10

Ates Goral


Here's a great script to do this:

https://github.com/matthewkastor/html-table-of-contents/wiki

To use it:

  1. Add this tag:

    <script src="./node_modules/html-table-of-contents/src/html-table-of-contents.js" type="text/javascript">
    
  2. Call the function, such as in your body's onload attribute:

    <body onload="htmlTableOfContents();"> 
    

Here is the definition of the method that does the generation:

/**
 * Generates a table of contents for your document based on the headings
 *  present. Anchors are injected into the document and the
 *  entries in the table of contents are linked to them. The table of
 *  contents will be generated inside of the first element with the id `toc`.
 * @param {HTMLDOMDocument} documentRef Optional A reference to the document
 *  object. Defaults to `document`.
 * @author Matthew Christopher Kastor-Inare III
 * @version 20130726
 * @example
 * // call this after the page has loaded
 * htmlTableOfContents();
 */
function htmlTableOfContents (documentRef) {
    var documentRef = documentRef || document;
    var toc = documentRef.getElementById('toc');
    var headings = [].slice.call(documentRef.body.querySelectorAll('h1, h2, h3, h4, h5, h6'));
    headings.forEach(function (heading, index) {
        var anchor = documentRef.createElement('a');
        anchor.setAttribute('name', 'toc' + index);
        anchor.setAttribute('id', 'toc' + index);

        var link = documentRef.createElement('a');
        link.setAttribute('href', '#toc' + index);
        link.textContent = heading.textContent;

        var div = documentRef.createElement('div');
        div.setAttribute('class', heading.tagName.toLowerCase());

        div.appendChild(link);
        toc.appendChild(div);
        heading.parentNode.insertBefore(anchor, heading);
    });
}

try {
     module.exports = htmlTableOfContents;
} catch (e) {
    // module.exports is not defined
}
like image 27
d13 Avatar answered Oct 05 '22 06:10

d13


JQuery comes to mind as a fast and easy solution. A quick google search for jquery table of contents yields two promising results:

  • jqTOC
  • Article on how to do this manually
like image 31
Francis Beaudet Avatar answered Oct 05 '22 08:10

Francis Beaudet


I've modified the function in AtesGoral's accepted answer to output correctly nested lists and valid HTML5.

Just add the code below to your scripts and call TableOfContents(container, output); on load, where container is the class or id of your content element and output is the class or id of the TOC element. Default values are '#contents' and '#toc', respectively.

See http://codepen.io/aufmkolk/pen/RWKLzr for a working demo.

function TableOfContents(container, output) {
var toc = "";
var level = 0;
var container = document.querySelector(container) || document.querySelector('#contents');
var output = output || '#toc';

container.innerHTML =
    container.innerHTML.replace(
        /<h([\d])>([^<]+)<\/h([\d])>/gi,
        function (str, openLevel, titleText, closeLevel) {
            if (openLevel != closeLevel) {
                return str;
            }

            if (openLevel > level) {
                toc += (new Array(openLevel - level + 1)).join('<ul>');
            } else if (openLevel < level) {
                toc += (new Array(level - openLevel + 1)).join('</li></ul>');
            } else {
                toc += (new Array(level+ 1)).join('</li>');
            }

            level = parseInt(openLevel);

            var anchor = titleText.replace(/ /g, "_");
            toc += '<li><a href="#' + anchor + '">' + titleText
                + '</a>';

            return '<h' + openLevel + '><a href="#' + anchor + '" id="' + anchor + '">'
                + titleText + '</a></h' + closeLevel + '>';
        }
    );

if (level) {
    toc += (new Array(level + 1)).join('</ul>');
}
document.querySelector(output).innerHTML += toc;
};
like image 33
Hendrik Avatar answered Oct 05 '22 07:10

Hendrik