Rendering table of contents in Jekyll
Having a table of contents (TOC) makes an article more readable. Our goal is to automatically generate TOC out of the headings in an article, for a site based on Jekyll and hosted on GitHub Pages, which is the setup of this site.
Generating the TOC
The site generated by Jekyll is basically a set of static HTML pages. It is
therefore possible to collect the HTML heading elements (h1, h2, etc.) and
generate a TOC using JavaScript, as these libraries
do.
However, those libraries do not work well, if the heading elements are not only
used in the article, but also used in the header or footer of the site, which
is the case for this site. For example, the “Memory Spills” site title in the
footer is wrapped in a <h2>
tag by Jekyll. That means “Memory Spills” would
appear in the TOC, which is not nice.
Fortunately, rendering of table of contents (TOC) is supported by kramdown, which is Jekyll 3.0.0’s default Markdown processor, and, as a side note, the only supported Markdown processor on GitHub Pages. Adding the following code snippet directly after the front matter will do the trick:
Note that no empty lines shall be present between the {:toc}
tag and the
first paragraph of the article (My first paragraph.
in the code example), or
else the latter will not be included in the post.excerpt
variable, which is
used for multiple purposes on this site. First, the post.excerpt
variable is used in the
home layout to show an excerpt of the post content for each post, in addition to its post title. That way the
post content can be previewed on the home page(s), before a reader decides to
click the read more
button which links to the complete post content. Second,
the post.excerpt
variable is also used to populate the description meta
tags important for SEO.
Adding the TOC heading
The rendered TOC is just a list of article headings. It would be nice if it has the TOC heading “Contents” or “Table of contents”. It turns out this is a bit tricky.
With Markdown
To insert such a TOC heading, one can use the following markdown code:
This will insert a bold “Contents” line as the heading before the TOC, which is
nice, but the word “Contents” becomes a part of the post.excerpt
variable,
and is consequently shown as a part of the preview, which is not so nice.
Therefore, I had to cut off the extra Contents
string as follows:
Remember the ‘post.excerpt’ variable is also used to populate the description
meta tags. There I found no easy way to get rid of the extra Contents
string,
which is not nice.
With JavaScript
Therefore, I removed the **Contents**
heading from the
markdown, and came up with a JavaScript hack.
The above
code
can be included ({ % include javascript.html %}
) as the last line in
_includes/footer.html
, so that the <script>
tag appears immediately before
the closing </body>
tag.
This works well without poluting the post.excerpt
variable. But is it
possible to do this without the JavaScript hack?
With CSS
It turns out arbitrary text can be inserted using CSS, e.g.,
The complete solution is adding the following CSS code to this file:
This pure CSS solution touches only one file, and, as with
the JavaScript solution, it does not polute the post.excerpt
variable.
However, I am not sure how to deal with i18n, e.g., for an article written in Chinese, one would prefer to add “目录” instead of “Contents”. That could be easier with the JavaScript solution. If you have a good solution, please leave a comment. Thanks!