10 Oct 2021
Table of Contents in Eleventy
I set up my blog so that I could include a table of contents to long posts if I want to. It’s based on what headings I have. Currently, I’ve set it to work only with <h2>
tags, as I rarely go deeper than that.
There are three parts to this:
- Use npm plugin markdown-it-anchor to add an
id
attribute to every heading. - Set
toc: true
in the post’s frontmatter. - Use JS to pull all of the headings, generate a list of links, and insert at the top of the post.
Anchor Plugin
Using the plugin markdown-it-anchor adds an id tag to each heading so you can do in-page links. This article explains how to install and configure it.
For example, a heading titled ‘A Short Hike’ would look like:
<h2 id="a-short-hike">A Short Hike</h2>
Liquid Markup
In my posts template post.liquid
:
{% if toc %}
<details class="post__toc">
<summary class="post__toc-heading">Table of Contents</summary>
<ol class="post__toc-list"></ol>
</details>
{% endif %}
The <ol>
list is empty, because each item will be inserted dynamically.
I nest all of this inside {% if toc %}
so that it only shows up if I’ve designated toc: true
in the post’s frontmatter.
JavaScript
In post.liquid
:
- For every
h2
on the page, create a list item - Get the
id
of each heading - Create a link in each list item, with the heading as the text and the id as the link URL
- Insert this list item into the
<ol>
tag.
const headings = document.querySelectorAll("h2");
const tocList = document.querySelector(".post__toc-list");
function generateTOC() {
for (let i = 0; i < headings.length; i++) {
let listItem = document.createElement("li");
listItem.setAttribute("class", "post__toc-item");
let listURL = "#" + headings[i].getAttribute("id");
let listItemLink = document.createElement("a");
listItemLink.setAttribute("href", listURL);
listItemLink.setAttribute("class", "post__toc-link");
let listItemTitle = document.createTextNode(headings[i].innerText);
listItem.appendChild(listItemLink);
listItemLink.appendChild(listItemTitle);
tocList.appendChild(listItem);
}
}
generateTOC();