Automatic mini TOC with Scroll Spy in online documentation

In modern online documentation, the topics are often quite long and consist of multiple sections. A small table of contents of the page (mini TOC) that appears next to the text provides good orientation here. With an appropriate JavaScript library, such a mini TOC can be integrated fully automatically into all topics.

A script automatically searches for the headings in the topic, builds the mini TOC from them, and fully links it.

Example

The page that you are currently reading also has such a small table of contents (look at the top left of the browser window – the mini TOC is only visible if the browser window is large enough). All second-level headings (h2 elements in HTML) automatically appear there. The script continuously monitors the current scroll position in the text (scroll spy). A sidebar visualizes the current position within the topic when scrolling and thus provides additional orientation.

In an online documentation, the left side is often already filled by the overall table of contents of the entire documentation. In this case, you can also have the automatically generated mini TOC created on the right-hand side.

Implementation

The solution presented here uses the open-source JavaScript library scrollnav.js.

A major advantage of this library compared to other libraries of the same kind is that the h2 elements in the text do not have to have IDs here (not all authoring tools provide IDs in the headings or make it possible to set IDs manually). If the IDs are missing, the library generates the IDs automatically at runtime.

Step 1: In the head section of your page template, link to the JavaScript file of the library (adjust the path as necessary).


<script src="YOUR-PATH/scrollnav.min.umd.js"></script>

Step 2: Add the following CSS code to the CSS file used by your online documentation or to the head section of your page template.

This code contains both the styles for the mini TOC and codes to later place the body content and the mini TOC next to each other. The styles are defined so that the mini TOC is not visible in small browser windows that are less than 750 pixels wide or when printing.

With the settings of the example, the mini TOC appears to the right of the topic content.


.scroll-nav__list {
  margin: 0;
  padding-left: 20px;
  list-style-type: none;
  border-left: solid;
  border-width: 1px;
  border-color: #c3c3c3;  
}
.scroll-nav__item {
  display: flex;
  align-items: center;
  justify-content: left;
  height: auto;
  margin-top: 1em;
  margin-bottom: 1em;
}
.scroll-nav__item--active {
}
.scroll-nav__item--active a:link {}
.scroll-nav__item--active a:visited {}  
.scroll-nav__item--active a:hover {}    
.scroll-nav__item--active::before {
  border-left: 3px solid rgb(34,34,34);
  content: '';
  height: 3em;
  left: 0;
  position: absolute;
}
.scroll-nav__link {
}
.mymenu a:link {color: #000 !important; text-decoration: none !important;}
.mymenu a:visited {color: #000 !important; text-decoration: none !important;}
.mymenu a:hover {color: #006699 !important;; text-decoration: underline !important;}
.mymenu {
  width: 350px;
  margin-left: 100px;
  float: right;
  position: sticky; top: 110px;
}
.mycontainer {
  width: auto;
  overflow: hidden;
}
@media screen and (max-width: 750px) {
 .mymenu {display: none;}
}
@media print {
 .mymenu {display: none;}
}

Step 3: In your page template, place two HTML snippets at the beginning of a topic as a placeholder for the mini TOC to be generated. Between these two HTML snippets, you can optionally add some short text, which will appear above the generated table of contents. For example, you may add a heading labeled “On this page:” or something similar here.


<div class="mymenu">

</div>

Step 4: Enclose the content of the topic in the following two HTML snippets:


<div class="mycontainer">

</div>

Step 5: At the end of the body section of your page template, include the following JavaScript code, which executes the script. For the parameters that it contains, see the documentation that comes with the script.

Tip: Other than described in the script documentation, you should not execute the code until the page has fully loaded, including all images. Otherwise, the calculated positions may not always be precise and the script may not always scroll exactly to the correct positions. Therefore, wrap the whole thing in the command "window.onload".


<script>
 const content = document.querySelector('.mycontainer');
 const sidebarCurrent = document.querySelector('.mymenu');
 function onPageLoaded() {
  scrollnav.init(content, {
   sections: 'h2',
   subSections: '',
   insertTarget: sidebarCurrent,
   insertLocation: 'append',
   easingStyle: 'easeOutQuint'
  });   
 }
 if (window.addEventListener) {
    // for modern browsers
    window.addEventListener("load", onPageLoaded);
 } else if (window.attachEvent) {
    // for older versions of Internet Explorer
    window.attachEvent("onload", onPageLoaded);
 } 
</script>

Extra heading (1)

This is just a dummy section to produce another heading for demonstration.

##### ##### ##### ### ##### ########### ########## ##### ### ## ###### ########## ######### ##### #### ###### ##### ##### ## ## ######### ########## ### #### ######### ##### ####### ###### ######### ############ ##### ### ###### ######## ## #### ## ########### ####### ###### ##### ######## # ##### #### ######## ########### ###### ###### ######

##### ##### ##### ### ##### ########### ########## ##### ### ## ###### ########## ######### ##### #### ###### ##### ##### ## ## ######### ########## ### #### ######### ##### ####### ###### ######### ############ ##### ### ###### ######## ## #### ## ########### ####### ###### ##### ######## # ##### #### ######## ########### ###### ###### ###### ## ######## ## ####### ########## ### ## ###### # ###### ####### ############# #### ######## ######## ####### ### ### #### ####### ###### ##### ####### #######

##### ###### ###### ######## ## ###### ######## ### ####### ####### ### ######## ########## ########### ### #### ######## ## #### ### ###### ########### ###### ##### ## ##### ### #### #### ########### ######## ############ ### ###### ########## ##### ###### ####### ### #### ##### ##### ######### #### ### ##### ######## ########## ##### ##### ######## ####### ### ######### ##### ##### ####### ##### ####### ###### ######### #### ####### ### #### ###### ##### ####### ### ######## # ######### ##### ############ #### ####### #### ##### ### ######### #### ######### ### ####### ####### #### # #### ######## ############# ### ###### ###### ####### ## ####### ### ######## #### ##### ##### ## ####### ##### #### ######## ###### ######### ########## ######### ##### ## #### ######### #######.

Extra heading (2)

Yet another dummy section only for demonstration.

##### ###### ###### ######## ## ###### ######## ### ####### ####### ### ######## ########## ########### ### #### ######## ## #### ### ###### ########### ###### ##### ## ##### ### #### #### ########### ######## ############ ### ###### ########## ##### ###### ####### ### #### ##### ##### ######### #### ### ##### ######## ########## ##### ##### ######## ####### ### ######### ##### ##### ####### ##### ####### ###### ######### #### ####### ### #### ###### ##### ####### ### ######## # ######### ##### ############ #### ####### #### ##### ### ######### #### ######### ### ####### ####### #### # #### ######## ############# ### ###### ###### ####### ## ####### ### ######## #### ##### ##### ## ####### ##### #### ######## ###### ######### ########## ######### ##### ## #### ######### #######.

##### ##### ##### ### ##### ########### ########## ##### ### ## ###### ########## ######### ##### #### ###### ##### ##### ## ## ######### ########## ### #### ######### ##### ####### ###### ######### ############ ##### ### ###### ######## ## #### ## ########### ####### ###### ##### ######## # ##### #### ######## ########### ###### ###### ###### ## ######## ## ####### ########## ### ## ###### # ###### ####### ############# #### ######## ######## ####### ### ### #### ####### ###### ##### ####### #######