Generate a table of contents

Hans's picture
Thu, 2009-12-31 13:14 by Hans · Forum/category:

Table of contents

for this article

Introduction

If you create web pages, you probably know about the awkward maintenance of tables of contents, like the one right here. Is there no way to automate this?

If you don't want a ready-made solution like this, but are interested in the programming, please read A JavaScript tutorial instead.

If your web pages are created by your own PHP or other code, you could probably generate the table of contents (TOC) with them, but static pages or pages over whose generation you have no powers, like those provided through a Content Management System (CMS) are a different matter.

Here is one simple solution—a little piece of JavaScript code that does the work for you, provided, your page has headings tagged with <h1>, <h2>, <h3>, up to <h6>.

This program creates only a single-level TOC. In the simplest case, all you have to do is include the following line in your page, preferably in the header, but it works also anywhere in the body.

<script src="http://winhlp.com/jstoc.js"></script>

Do not rely on my server

This, of course, would work only when and as long as my server is running and the file is there, which is not guaranteed. Therefore, and to save me a little bandwidth, I ask you to download the file and put it on your own server. To download, right-click here and elect to save the target to your disk.

Assuming the file is in the document root of your own web server, the line above should be changed to:

<script src="/jstoc.js"></script>

Automatic operation

If you simply add the single line, the program behaves as follows.

It searches the entire page for headings, beginning with <h1>, up to <h6>. As soon as it finds two or more headings of the same level, it builds the TOC using these.

For example, if the page has one <h1> heading, two or more <h2> headings and any number of <h3> to <h6> headings, it will ignore the <h1> heading, as there is only one, will build the TOC for all <h2> headings, and will ignore all others.

It will automatically put the TOC directly above the first heading it used.

Formatting the TOC

The simplest way to apply formatting is CSS. For example, if your list elements (<li>) have vertical spacing, but in the TOC you want them spaced tightly, add this to your style sheet:

#jstoc li {
    margin: 0;
}

If you have no internal or external style sheet, add this before the closing head tag (</head>):

<style type="text/css">/*<![CDATA[*/
#jstoc li {
    margin: 0;
}
/*]]>*/</style>

If you want the TOC to be differently placed, insert a div element in the page where you want the TOC to be and format it to your liking. It has to have the id "jstoc", so the program can find it among other divs that may be in the page. Example:

<div id="jstoc">
<p style="font-weight: bold">Table of Contents</p>
</div>

Example for a floating-right TOC as used at the top of this article:

<div id="jstoc" style="clear: both; float: right; width: 45%; border: 1px solid #8ad; margin: 0.3em 0 0.2em 0.2em; padding-right: 0.2em; padding-bottom: 0.7em;">
<p style="margin-bottom: 0; text-align: center; font-weight: bold; color: darkblue">Table of contents</p>
<p style="margin-top: 0; text-align: center; font-size: smaller; color: darkblue;">for this article</p>
</div>

If you want the floating left or right TOC to assume the width of its longest entry without line breaks automatically, just remove the width parameter from its style.

Numbering

You can also have the TOC numbered. To do that, add an <ol> tag inside the div with the id "jstoc", normally at the end, just before the closing </div> tag. The program will then also number the headings themselves, i.e. put a number and a period in front of each.

Technical background: If no <ol> or <ul> element is found inside the div, the program automatically inserts an <ul> element at the end, that is why you don't have to provide one. But if you do provide an <ul> element, the program will gladly use it, which can be useful if you apply CSS formatting to it. If it detects an <ol> tag, it will also number those headings from which the TOC is built.

Not all headings

When you provide a div element with the id "jstoc", then the program does some additional work. It looks for the parent HTML element, i.e. the directly surrounding element of the div and works only inside it, while ignoring everything outside.

In rare cases the headings may be grouped, and not all desired headings may be in that group. In this case you will notice that not all headings are incorporated in the TOC. In this case you can instruct the program to look for the grandparent, the grand-grandparent, and so on, by providing a class attribute named more1 or more2 or more3 or, for example, more12 to the jstoc div. Example:

<div id="jstoc" class="more1">
<p style="font-weight: bold">Table of Contents</p>
</div>

If you already use the class attribute, for example for CSS formatting, just add one of the more… keywords to the class attribute, separated from any others by some white space. Example:

<div id="jstoc" class="my-toc-css more1">
<p style="font-weight: bold">Table of Contents</p>
</div>

The order of the class attribute components plays no role, so class="my-toc-css more1" works just as well as class="more1 my-toc-css".

Conversely you can use this feature to limit the TOC to a certain part of the document. Just put that part into a div, i.e. add <div> above and </div> below the desired part and do not use the "more…" class entry. The jstoc div must be inside that delimiting div.

The following is an example of a document with two <h1 …> elements, followed by two or more <h2 …> elements. Normally jstoc would find the h1 elements first and use them to construct the TOC, but if you want a TOC for the h2 elements instead, do this:

<h1>…</h1>

<h1>…</h1>

<div>
<div id="jstoc" style="…">…</div>
<h2>…</h2>

<h2>…</h2>

<h2>…</h2>

</div>

Note that the div with the id="jstoc" must be inside the additional divs, shown in red.

Jumping to a chapter

You will notice that the program adds anchors to the headings that are constructed from the first letters of the first up to five words in the heading. For example, to jump to this chapter you are reading right now, the link is: #jtac

You can even hand this over as an external link, like: http://winhlp.com/node/682#jtac

It seems obvious, but in fact it isn't, because when that link is called and the page opened, the TOC is not yet there, and neither is any of the anchors, so no jumping will take place. Most other TOC programs you can find don't have this capability.

It all works only because of a little magic in the background. The program, after building the TOC, checks the URL again for the hash character #. If there is one, it uses the text after it and jumps to the anchor.

Closing remarks

I'm pondering the thought of adding two-level or multi-level TOCs. That is certainly doable, but the question is whether the added complexity is worth the effort.

Please let me know if you find any defects in the program.