TOC Animated Side Navigation For Long Web Content

Progress Nav is a nice side table of contents menu and navigation system that allows the current navigation element to be marked with an animated progress line when scrolling through a web page.

responsive table of contents, html table of content, tocify title table of contents, animated side navigation bar

Pure CSS Progress Bar Scroll Depth Indicator Library

How to make use of it:

1. Create a table of contents and navigation with corresponding content sections and an SVG-based progress bar.

<nav class="toc">
  <ul>
    <li><a href="#section1">Section One</a></li>
    <li>
      <a href="#section2">Section Two</a>
      <ul>
        <li><a href="#section21">Section 2-1</a></li>
        <li><a href="#section22">Section 2-2</a></li>
        <li><a href="#section23">Section 2-3</a></li>
      </ul>
    </li>
  </ul>
  <svg class="toc-marker" width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <path stroke="#444" stroke-width="3" fill="transparent" stroke-dasharray="0, 0, 0, 1000" stroke-linecap="round" stroke-linejoin="round" transform="translate(-0.5, -0.5)" />
  </svg>
</nav>
<article class="contents">
  <section id="section1">
    <h2>Section One</h2>
  </section>
  <section>
    <div id="section2">
      <h2>Section Two</h2>
    </div>
    <div id="section21">
      <h3>Section 2-1</h3>
    </div>
    <div id="section22">
      <h3>Section 2-2</h3>
    </div>
 </section>
 ......
</article>

2. Design the table of contents and navigation.

.toc {
  position: fixed;
  left: 3em;
  top: 5em;
  padding: 1em;
  width: 14em;
  line-height: 2;
}
.toc ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.toc ul ul { padding-left: 2em; }
.toc li a {
  display: inline-block;
  color: #aaa;
  text-decoration: none;
  -webkit-transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
  transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
}
.toc li.visible > a {
  color: #111;
  -webkit-transform: translate(5px);
  transform: translate(5px);
}
.toc-marker {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
}
.toc-marker path {
  -webkit-transition: all 0.3s ease;
  transition: all 0.3s ease;
}

3. Main javascript:

var toc = document.querySelector( '.toc' );
var tocPath = document.querySelector( '.toc-marker path' );
var tocItems;
// Factor of screen size that the element must cross
// before it's considered visible
var TOP_MARGIN = 0.1,
    BOTTOM_MARGIN = 0.2;
var pathLength;
window.addEventListener( 'resize', drawPath, false );
window.addEventListener( 'scroll', sync, false );
drawPath();
function drawPath() {
  
  tocItems = [].slice.call( toc.querySelectorAll( 'li' ) );
  // Cache element references and measurements
  tocItems = tocItems.map( function( item ) {
    var anchor = item.querySelector( 'a' );
    var target = document.getElementById( anchor.getAttribute( 'href' ).slice( 1 ) );
    return {
      listItem: item,
      anchor: anchor,
      target: target
    };
  } );
  // Remove missing targets
  tocItems = tocItems.filter( function( item ) {
    return !!item.target;
  } );
  var path = [];
  var pathIndent;
  tocItems.forEach( function( item, i ) {
    var x = item.anchor.offsetLeft - 5,
        y = item.anchor.offsetTop,
        height = item.anchor.offsetHeight;
    if( i === 0 ) {
      path.push( 'M', x, y, 'L', x, y + height );
      item.pathStart = tocPath.getTotalLength() || 0;
    }
    else {
      // Draw an additional line when there's a change in
      // indent levels
      if( pathIndent !== x ) path.push( 'L', pathIndent, y );
      path.push( 'L', x, y );
      
      // Set the current path so that we can measure it
      tocPath.setAttribute( 'd', path.join( ' ' ) );
      item.pathStart = tocPath.getTotalLength() || 0;
      
      path.push( 'L', x, y + height );
    }
    
    pathIndent = x;
    
    tocPath.setAttribute( 'd', path.join( ' ' ) );
    item.pathEnd = tocPath.getTotalLength();
  } );
  
  pathLength = tocPath.getTotalLength();
  
  sync();
  
}
function sync() {
  
  var windowHeight = window.innerHeight;
  
  var pathStart = Number.MAX_VALUE,
      pathEnd = 0;
  
  var visibleItems = 0;
  
  tocItems.forEach( function( item ) {
    var targetBounds = item.target.getBoundingClientRect();
    
    if( targetBounds.bottom > windowHeight * TOP_MARGIN && targetBounds.top < windowHeight * ( 1 - BOTTOM_MARGIN ) ) {
      pathStart = Math.min( item.pathStart, pathStart );
      pathEnd = Math.max( item.pathEnd, pathEnd );
      
      visibleItems += 1;
      
      item.listItem.classList.add( 'visible' );
    }
    else {
      item.listItem.classList.remove( 'visible' );
    }
    
  } );
  
  // Specify the visible path or hide the path altogether
  // if there are no visible items
  if( visibleItems > 0 && pathStart < pathEnd ) {
    tocPath.setAttribute( 'stroke-dashoffset', '1' );
    tocPath.setAttribute( 'stroke-dasharray', '1, '+ pathStart +', '+ ( pathEnd - pathStart ) +', ' + pathLength );
    tocPath.setAttribute( 'opacity', 1 );
  }
  else {
    tocPath.setAttribute( 'opacity', 0 );
  }
}

Animated Side TOC Nav For Long Web Content Plugin/Github


See Demo And Download

Official Website(hakimel): Click Here

This superior jQuery/javascript plugin is developed by hakimel. For extra Advanced Usages, please go to the official website.

Related Posts

svg-pan-zoom-container

Adding Zoom-on-Wheel and Pan-on-Drag to Inline SVG Elements

Vanilla-js module for adding zoom and panning behavior when dragging to SVG embedded elements. A lightweight Vanilla JavaScript plugin that enables zoom and pan functions on an…

html-table-sortable-js

Sorting HTML Table Vanilla JavaScript Library | Sortable.js

Sortable – Small JS vanilla table sorter makes any table with class = “sortable“, er, sortable. That is, the user can click the table header and change…

WYSIWYG-Rich-Text-Editor

WYSIWYG Rich Text Editor With jQuery And FontAwesome | RichText

RichText jQuery implementation for WYSIWYG Rich Text Editor which uses Font Awesome Iconic Font for editor icons. It is licensed under AGPL-3.0. Initialize editor Simply call .richText() on your jQuery(‘textarea’) or jQuery(‘input’)…

email-domain-autocomplete-genie

[Autocomplete] Email Domain Suggestions Like Gmail, Outlook, or More | email-genie

Email Genie allows auto-completion in the email field by providing a list of domain suggestions (gmail.com, outlook.com, etc.). This package is lightweight, flexible, compatible with libraries (jQuery,…

JavaScript-Library-for-Creating-Squircley-Magic

[Generator] JavaScript Library for Creating Squircley Magic ✨ | squircley.js

squircley.js is the core magic of Squirclular ✨ from https://squircley.app wrapped in a simple 0-dependency JavaScript library. squircley.js can generate SVG files, add square backgrounds to DOM…