Simple TOC with scrollspy in 20+10 lines javascript
Tags: javascript, document, toc, scrollspy
Published: 2021-10-08 07:04:51

Noted many document sites have fancy feature of TOC with scrollspy, really want to have it on my document project. 

Though there are some js libraries available (like of [tocify](http://gregfranko.com/jquery.tocify.js/) or [Tocbot](http://tscanlin.github.io/tocbot/)), I do want to have a simple solution with pure javascript.

Finally work out the technical trick, it is quite simple.

First, retrieve all "H2,H3,H4" elements, generate "table of contents" by ``<ul><li><a href=#headerID>Header Text</a></li>...</ul>``, and put it to TOC container. it is quite easy by less than 20 lines of javascript. 

The only trick is using ``style="margin-left:12px`` to get indent effect.

<small><i>ps: only use "h2,h3" tags for TOC in my project.</i></small>

```
//=== simpleTOC: show Table of Content
function simpleTOC( title, srcDiv, toDiv ) {
  var toc = document.getElementById(srcDiv||'right-panel').querySelectorAll('h2,h3')
  var html = '<h4> ' + (title||'Content') + '</h4><ul id="toc">';

  for (var i=0; i<toc.length; i++ ) {
  
  	if (!toc[i].id) toc[i].id = "toc-item-" + i;
    
  	if (toc[i].nodeName === "H2" && toc[i].id.substr(0,6)!=="no-toc") {
  		html += '<li style="background:#f6f6f6"><a href="#' + toc[i].id + '">' + toc[i].innerText + '</a></li>';
  	} else if (toc[i].nodeName === "H3" && toc[i].id.substr(0,6)!=="no-toc") {
  		html += '<li style="margin-left:12px"><a href="#' + toc[i].id + '">' + toc[i].innerText + '</a></li>';
  	} else if (toc[i].nodeName === "H4" && toc[i].id.substr(0,6)!=="no-toc") {
  		html += '<li style="margin-left:24px"><a href="#' + toc[i].id + '">' + toc[i].innerText + '</a></li>';
  	}
    
  }

  document.getElementById(toDiv||'left-panel').innerHTML = html   
}
```

Scrollspy is a fancy feature seems difficult, but work out quite simple in 10 lines javascript.

just simple declare function listening onscroll() event, check the position of all TOC items by `position := Element.offsetTop - DIV.scrolltop`, highlight these TOC items shown in viewport (ie. ``position>0 and position<DIV.height``). that's all.

```
//=== scrollspy feature
document.getElementById('right-panel').onscroll = function () {
  var list = document.getElementById('left-panel').querySelectorAll('a')
  var divScroll = document.getElementById('right-panel').scrollTop - 10
  var divHeight = document.getElementById('right-panel').offsetHeight
  
  for (var i=0; i<list.length; i++) {
    var div = document.getElementById( list[i].href.split('#')[1] )
    var pos = (div? div.offsetTop - divScroll : 0 )  // in case of not found sometimes.
    list[i].style['font-weight'] = ( pos>0 && pos<divHeight ? 600 : 400 )
  }
}
```

sorry for bad coding style (the code may not fully reusable, but easy to migrate to your project with minimum revision).

-------------------------------------------
* source code: [github.com/casualwriter/powerpage-document](https://github.com/casualwriter/powerpage-document/blob/main/index.html)
* sample site: [Powerpage Documentation](https://pingshan-tech.com/powerpage/doc/)
* relate project: [Powerpage Document Framework](https://github.com/casualwriter/powerpage-document)