2016/06/28

Table of contents generator in javascript

There are a lot of approaches arround. Lets add one more to the pile:

Javascript

No need of jquery or other stuff. Just plain and simple vanilla js ;]

function TableOfContents(tocId, containerId) {
    var toc = document.getElementById(tocId);
    toc.innerHTML = "";
    toc.className += " toc";
    var container = containerId ? document.getElementById(containerId) : toc.parentElement;
    var headings = [].slice.call(container.querySelectorAll('h1, h2, h3, h4, h5, h6'));
    var closeLevel = function(e, levels) {
        for (var i = 0; i < levels && e.parentElement && e.parentElement.parentElement; i++) {
            e = e.parentElement.parentElement;
        }
        return e;
    };
    var createLiWithAnchor = function(anchor, heading) {
        var li = document.createElement("li");
        var a = document.createElement("a");
        a.href = "#" + anchor;
        a.textContent = heading.textContent;
        li.appendChild(a);
        heading.innerHTML = "<a name='" + anchor + "'>" + heading.textContent + "</a>";
        heading.className += " no-decoration";
        return li;
    };
    var prevLevel = 0;
    var root, curr;
    headings.forEach(function (heading, index) {
        var tag = heading.tagName.toLowerCase();
        var curLevel = parseInt(tag.replace(/[^\d]/i, ""));// get number from h1, h2, h3,... tags
        var anchor = heading.textContent.replace(/\r?\n|\r/g, "").replace(/\s/, "_"); // remove all new lines and replace spaces with underscore
        var li = createLiWithAnchor(anchor, heading);
        if (curLevel > prevLevel) {
            // open 1 ul and add 1 li
            if (!curr) {
                root = document.createElement("ol");
                root.appendChild(li);
            } else {
                var ul = document.createElement("ul");
                ul.appendChild(li);
                curr.appendChild(ul);
            }
        } else if (curLevel === prevLevel) {
            // add 1 new li next to current li
            curr.parentElement.appendChild(li);
        } else if (curLevel < prevLevel) {
            // close n ul, add one 1 li as a sibling of the ancestor
            var ancestor = closeLevel(curr, prevLevel - curLevel);
            ancestor.parentElement.appendChild(li);
        }
        curr = li;
        prevLevel = curLevel;
    });
    toc.appendChild(root);
}
If you are pasting this into blogger, the function must be XML escaped. This site helped a lot

Css

So it looks a bit better.
/* ----- TOC ----- */
.toc {
background-color: #dadada;
border: gray solid 1px;
margin-bottom: 1em;
}
.toc .toc-title {
margin-left: 1em;
}
.post-body .no-decoration,
.post-body .no-decoration a {
text-decoration: none;
color: black;
}

Usage

Prepare a container

<div id="mytoc"></div>

Call the script

<script type="text/javascript">
TableOfContents("mytoc", "articleId");
</script>
If you using blogger just put the container div at the top of your post and omit the last parameter, the function will assume the article is the parent of the TOC container.

Result

0 comments :