is a tiny script that parses all headlines on a page and creates a table of contents from it.
I've written this for use on a Jekyll site, but there is nothing Jekyll-specific about it.
There's also ghiculescu/jekyll-table-of-contents; it didn't quite fit my needs, but it might fit yours.
This script has three advantages:
it has no dependencies
jekyll-table-of-contents depends on jQuery, which I do not use
it automatically assigns IDs to headlines, if necessary, to make them linkable
its markup is a bit easier to customize
<div id="my-toc-container"></div>
document.addEventListener('DOMContentLoaded', () => {
// basic
new Toctoctoc({ elem: document.querySelector("#my-toc-container") });
// with options
new Toctoctoc({
elem: document.querySelector("#my-toc-container"),
// limit the part of the document in which we look for headlines
scope: document.querySelector("body > main"),
// custom selector for headlines
headlineSelector: "h2, h3, h4, h5, h6, h7, h8"
});
});
By default, we render a simple unordered list:
<ul class="toc">
<li><a href="#headline-0">Headline</a></li>
<li>
<ul class="toc">
<li><a href="#headline-1">Second headline</a></li>
</ul>
</li>
</ul>
If you need something different, you can do so via three callbacks:
renderListCallback
provides the list - by default the <ul class="toc">
renderListItemCallback
provides the item wrapper - by default the <li>
renderListEntryCallback
provides the item itself - by default the <a>
renderListCallback({ callback, indentationLevel })
renderListCallback must return a list node. It must also call the provided callback with a DOM node in which the list items are to be inserted, which is not necessarily the same. Consider this example in which we wrap the <ul>
in a <div>
- but only the first, outer one:
new Toctoctoc({
elem: document.querySelector("#my-toc-container"),
renderListCallback: ({ callback, indentationLevel }) => {
if (indentationLevel === 1) {
const div = document.createElement("div"),
list = document.createElement("ul");
div.appendChild(list);
callback(list);
return div;
} else {
const list = document.createElement("ul");
callback(list);
return list;
}
}
});
renderListItemCallback()
renderListItemCallback must simply return a list item node:
new Toctoctoc({
elem: document.querySelector("#my-toc-container"),
renderListItemCallback: () => {
const item = document.createElement("li");
item.classList.add("foo");
return item;
}
});
renderListEntryCallback(headlineDOMNode)
renderListEntryCallback receives the current headline node and must simply return the content of the list item:
new Toctoctoc({
elem: document.querySelector("#my-toc-container"),
renderListEntryCallback: (headline) => {
const a = document.createElement("a");
a.href = `#${elem.id}`;
a.textContent = elem.textContent;
return a;
}
});
toctoctoc is licensed under the EUPL v1.2