How to add a Table of Contents automatically (without jQuery)
When I published the Professional Australian Glossary I decided to add a table of contents. It was a large article.
But I thought… there’s got to be a way of automating this. And if there isn’t, I’ll build one!
I pieced this together from around the web, but had to redo it a bit to make it work with updated versions of Ghost.
Table of Contents in Blogs — Summary
Tables of contents are almost mandatory to making a blog post easier to read. There are plugins for WordPress, but no easy way to do it on a Ghost blog.
Sub-heading
This is a lower-level nested block, to make sure it works with h3 and below headings.
How to install
Put this code at the bottom in an HTML block. This looks through the code and finds all the headings (tagged with <h2>
or <h3>
… did you use headings?) and creates the contents.
Here’s the code below. It’s in pure JavaScript, not using jquery.
I wrote this version because I felt like loading extra resources was lazy, and not all themes use it (and I wouldn’t want to rely on whatever version they were using anyway).
Secondly, this is pretty basic DOM manipulation and I thought: why not just do it in pure JS anyway? ES6 is amazing, anyway.
<script>
document.addEventListener('DOMContentLoaded', function () {
var contentsTitle = "Contents";
var ToC = "<h2>" + contentsTitle + "</h2>";
ToC += '<nav role="navigation" class="table-of-contents"><ul>';
// Select the <article> element
var article = document.querySelector('article');
// Find the first <h2> element within the <article>
var firstH2 = article.querySelector('h2');
// Create a container for the table of contents
var tocContainer = document.createElement("div");
tocContainer.id = 'dynamictocnative';
// If there's an <h2> element, insert the ToC container before it
if (firstH2) {
firstH2.parentNode.insertBefore(tocContainer, firstH2);
} else if (article) {
// If there's no <h2>, but an <article>, append the ToC container to it
article.appendChild(tocContainer);
}
// Find all <h2> and <h3> elements within the <article>
var headers = article.querySelectorAll('h2,h3');
headers.forEach(function(el, index) {
var title = el.textContent;
var link = '#' + el.id;
if (el.nodeName === 'H2') {
ToC += '<li><a href="' + link + '">' + title + '</a></li>';
} else if (el.nodeName === "H3") {
ToC += '<li style="margin-left:2em"><a href="' + link + '">' + title + '</a></li>';
}
});
ToC += '</ul></nav>';
ToC += '<style>#dynamictocnative { width: 100%; }</style>';
// Insert the table of contents into the container
var tocDiv = document.getElementById('dynamictocnative');
if (tocDiv) {
tocDiv.innerHTML = ToC;
}
});
</script>
How it works
Here’s how this table of contents script works, in a nutshell.
- We create a new
<div>
element with the id ‘dynamictocnative’ to serve as the container for the table of contents. - We then find the first
<h2>
element within the<article>
element. - If an
<h2>
element is found, we useinsertBefore
to insert the table of contents container before this<h2>
element. If no<h2>
is found but there’s an<article>
element, we append the ToC container to the<article>
element instead. - We then proceed as before to create the table of contents based on the
<h2>
and<h3>
elements within the<article>
. - Finally, we insert the table of contents into the ‘dynamictocnative’ container.
This script assumes that there’s at least one <article>
element and uses the first one it finds. If your page structure is different, you may need to adjust the selectors. But most modern CMS designs use <article>.
Next steps
It might be nice to make this a separate js plugin so you can embed it on a website. But for now, this is just a proof of concept, anyway.
If you like, you can put it in a js file on your local server, and include it.
I’m a 93 year old Australian born and have been living in Italy for 60 years. I got the idea of compiling a dictionary of Australian Slang for Italians so that an Italian tourist could get around better by knowing a few everyday words such as arvo, good on you, hooroo and their Italian equivalent. The basic idea of this dictionary could be applied later on to other tourist groups intending to visit Australia, such as French, German, Dutch and others.
My work has now passed from a small hand book for tourists to a reference book for translators of novels or films. To make my project complete it needs to be converted to an audio dictionary with real-time response.
If you have any idea on how to accomplish this project I woulld be happy to hear from you.
I think you meant to comment on this post about Australian slang.
This sounds fun. I’ll send you a note. How’d you end up living in Italy? That’s the real story! I’d love to move there, but alas post-Brexit that option is off the table (I’m British/Australian).