Automatically generate table of contents using jQuery

Some time ago, I was debating with my friends on the topic: is there any use of generating table of contents automatically. The conclusion was that it can be useful in cases when the reading material is long enough and table of contents (TOC) has a fixed position on the screen. This tutorial will show you how to create such TOC in just a few lines of code.

Extract information from HTML

I will start with the basic example – where we just want to get title and subtitles and show them before the article itself.

<div id="toc"></div>
    <div id="content">
    <h1>Title goes her</h1>
    <h2>Subtitle goes here</h2>
    <p>Text goes here...</p>
</div>

Div element with “toc” id will be a container for TOC links (this can be also added dynamically but for this example, we’ll keep it in static structure). The actual article is placed in div element with id “content”. Check out demo 1 for full lorem ipsum article. Now let’s create TOC. First we’ll append a paragraph inside “toc” container with text “In this article”. You might have seen this in some blog posts around. Next, we’ll find all H1, H2 and H3 elements and assign unique id (for this page) to each one of them. This will make them easily accessible on click. At the end, we’ll append a link for each heading.

$("#toc").append('<p>In this article:</p>')
$("h1, h2, h3").each(function(i) {
    var current = $(this);
    current.attr("id", "title" + i);
    $("#toc").append("<a id='link" + i + "' href='#title" +
        i + "' title='" + current.attr("tagName") + "'>" + 
        current.html() + "</a>");
});

The output of this code looks like this:

<div id="toc">
<p>In this article:</p>
<a id="link0" title="H1" href="#title0">Article title</a>
<a id="link1" title="H2" href="#title1">Header Level 2</a>
<a id="link2" title="H3" href="#title2">Header Level 3</a>
<a id="link3" title="H3" href="#title3">Header level 3 again</a>
<a id="link4" title="H3" href="#title4">Header level 3 once again</a>
</div> 

And headings will have id’s like in the example below:

<h1 id="title0">Article title</h1>

View demo 1

Fixing TOC position

In order to fix the position of TOC we have to do two things. First one is to wrap TOC and content into a div and center it on the screen (you can place TOC anywhere you like, but in this example we’ll fix it to the left of the content).

<div id="container">
    <div id="toc"></div>
    <div id="content">
        <h1>Title goes her</h1>
        <h2>Subtitle goes here</h2>
        <p>Text goes here...</p>
    </div>
</div> 

Next, set #toc to float left with fixed position and #content to float right.

#container { width:960px; overflow:hidden; margin:0px auto; position:relative;}
#content { width:660px; float:right;}
#toc { width:200px; position:fixed; float:left;}
#toc a { display:block; color:#0094FF;}

This will ensure that TOC remains on the screen on a fixed position while scrolling.

View demo 2

Scaled Table of Contents

The idea is to get positions of all headings and scale them vertically on a page. This can give users the idea of how long sections are.

View demo 3

We would have to modify the script from previous demo:

$("h1, h2, h3").each(function(i) {
    var current = $(this);
    current.attr("id", "title" + i);
    
    var pos = current.position().top / $("#content").height() * $(window).height();
    $("#toc").append("<a id='link" + i + "' href='#title" + i +
    "' title='" + current.attr("tagName") + "'>" +
    current.html() + "</a>");
    
    $("#link" + i).css("top", pos);
});

This code determines the position of each heading (current.position().top) and scale it to fit in the window. After appending HTML anchor, a new position is assigned via CSS. Finally, we have to position HTML anchors absolutely. Also, each heading type has different font size. The CSS in demo 3 looks like this:

#toc { width:200px; position:fixed; float:left; position:fixed;
    top:0px; background:transparent url(bkg.png) right; height:100%;}
#toc a { display:block; position:absolute; width:190px; text-align:right;
    background:transparent url(chapter_bullet.png) no-repeat right;
    color:#0094FF;  padding-right:10px;}
#toc a[title=H1] { font-size:18px;}
#toc a[title=H2] { font-size:14px;}
#toc a[title=H3] { font-size:10px;}

This is an interesting concept and would be even more interesting if the area between headings won’t be empty. Instead, it can show scaled preview of each paragraph. But this is a topic for other tutorial. What do you think?