DEV Community

Casualwriter
Casualwriter

Posted on

Simple TOC with scrollspy in 20+10 lines javascript

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 or 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.

ps: only use "h2,h3" tags for TOC in my project.

//=== 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   
}
Enter fullscreen mode Exit fullscreen mode

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 )
  }
}
Enter fullscreen mode Exit fullscreen mode

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


Top comments (0)