On our site, we use custom JS code to create a page TOC (“mini TOC”) from h2
headings on the page, instead of Hugo’s TableofContents
partial (unless the custom toc
page param is set to hugo
). To my recollection, the main incentive for the custom implementation was to also include in the mini TOC headings created with shortcodes. I originally shared our implementation in response to a question in Shortcode processing order, as this implementation also works to include in the TOC headings from a file that is included using a shortcode.
list.html & single.html Theme Default Partials
The theme _default partials include this code:
{{ partial "mini-toc" . }}
mini-toc.html Theme Partial
{{ $headers := findRE "<h[2].*?>(.|\n])+?</h[2]>" .Content }}
{{ if and (ge (len $headers) 1) (ne $.Params.toc "none") }}
<div class="toc">
<div class="mini-toc">
<div class="mini-toc-header">
<i class="fa fa-caret-right laptop" aria-hidden="true"></i>
<span>On This Page</span>
<i class="fa fa-caret-right desktop" aria-hidden="true"></i>
</div>
{{ if eq $.Params.toc "hugo" }}
{{ .TableOfContents }}
{{ else }}
<nav role="navigation" id="TableOfContents">
<ul class="toc-js"></ul>
</nav>
{{ end }}
</div>
</div>
{{ end }}
mini-toc.js
// Create a custom page TOC ("mini TOC")
function buildMiniToc() {
var ToC;
$('.content h2').each(function (i, el) {
// Replace <` and `>` in the HTML headings with `<` and `>` to
// support using these character entities in the source heading text and
// avoid interpreting them as HTML tags. The current drawback is that
// escaped `\<` or `\>` uses in the source heading text will appear
// in them mini-TOC as `<` and `>` instead of `<` and `>`.
var title = $(el).text().replace(/</g, '<').replace(/>/g, '>');
var link = '#' + $(el).attr('id');
ToC = '<li><a href="' + link + '">' + title + '</a></li>';
// Display the mini TOC only if page has TOC-level headings (currently, h2)
$('ul.toc-js').append(ToC);
});
$(".mini-toc-header").click(function() {
$("#TableOfContents").slideToggle(200);
$(".mini-toc i").toggleClass("fa-caret-right");
$(".mini-toc i").toggleClass("fa-caret-down");
});
$(window).scroll(function() {
var windScroll = $(this).scrollTop();
windScroll > 200 ? $('#scroll-top').show() : $('#scroll-top').hide();
if (windScroll) {
$(".doc-content > h2, h3, h4").each(function() {
if ($(this).position().top <= windScroll + 56) {
var activeHeader = $("#TableOfContents").find('[href="#' + $(this).attr('id') + '"]');
if (activeHeader.length) {
$("#TableOfContents").find("a").removeClass("active");
activeHeader.addClass("active");
}
}
})
}
}).scroll();
$(window).resize(function() {
if ( $(window).width() > 1143 ){
$('.mini-toc-header i').removeClass('fa-caret-right').addClass('fa-caret-down');
$('#TableOfContents').show();
} else {
$('.mini-toc-header i').removeClass('fa-caret-down').addClass('fa-caret-right');
$('#TableOfContents').hide();
}
}).resize();
}