Author: | hakimel |
---|---|
Views Total: | 2,096 views |
Official Page: | Go to website |
Last Update: | January 13, 2017 |
License: | MIT |
Preview:

Description:
This is a pretty cool side TOC (table of contents) & navigation system which allows to highlight the current nav item with an animated progress line when you scroll through the webpage.
How to use it:
Create the TOC & Navigation together with the corresponding content sections and SVG based progress bar on the webpage as follows:
<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>
Style the TOC & 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; }
The 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 ); } }