Last active
February 10, 2026 23:54
-
-
Save indikatordesign/f040ec516ae7ca0ced519bc120fff5a4 to your computer and use it in GitHub Desktop.
WWL TOC Blog Animation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* ----------------------------- TOC -------------------------- */ | |
| /* Im span wird "^" verwendet, mit einem Icon tauschbar ------- */ | |
| /* In delta wird die genaue Zielposition festgelegt ----------- */ | |
| /* --------------------------- jQuery ------------------------- */ | |
| (function($,w){$(function(){"use strict"; | |
| $( '.toc-header' ).wrap( '<div class="toc wrap" />' ) | |
| .after( '<span class="toc-item toc-icon header">^</span>' ) | |
| $( '#toc-ol li, div.toc.wrap' ).on( 'click', function( e ) | |
| { | |
| e.preventDefault() | |
| e.stopPropagation() | |
| let delta = 15, | |
| t = $( this ), | |
| wid = ( $( w ).width() < 981 ? 0 : 90 ) + delta, | |
| elem, index | |
| if ( t.hasClass( 'wrap' ) ) | |
| elem = $( '#toc' ) | |
| else | |
| { | |
| index = $( '#toc-ol li' ).index( t ) | |
| elem = $( '.toc-header' ).eq( index ) | |
| } | |
| if ( elem.length < 1 ) | |
| return false | |
| $( 'html, body' ).animate({ scrollTop: elem.offset().top - wid }, 200, 'swing', function() { return }) | |
| }); | |
| });}(jQuery,window)); | |
| /* -------------------------- Vanilla JS ---------------------- */ | |
| (function( w ) | |
| { | |
| w.addEventListener( 'DOMContentLoaded', function() | |
| { | |
| new wwlTOC | |
| } ) | |
| }( window )) | |
| class wwlTOC | |
| { | |
| constructor() | |
| { | |
| this.setProperties() | |
| this.initialize() | |
| } // end constructor | |
| setProperties() | |
| { | |
| this.delta = 15 | |
| this.duration = 200 | |
| this.bound = false | |
| } // end setProperties | |
| initialize() | |
| { | |
| this.wrapHeaders() | |
| this.bindEvents() | |
| } // end initialize | |
| wrapHeaders() | |
| { | |
| const headers = document.querySelectorAll( '.toc-header' ) | |
| if ( headers.length < 1 ) | |
| return false | |
| headers.forEach( ( header ) => | |
| { | |
| if ( header.closest( 'div.toc.wrap' ) ) | |
| return | |
| const wrap = document.createElement( 'div' ) | |
| wrap.className = 'toc wrap' | |
| header.parentNode.insertBefore( wrap, header ) | |
| wrap.appendChild( header ) | |
| const next = header.nextElementSibling | |
| if ( next && next.classList && next.classList.contains( 'toc-icon' ) ) | |
| return | |
| header.insertAdjacentHTML( | |
| 'afterend', | |
| '<span class="toc-item toc-icon header">^</span>' | |
| ) | |
| } ) | |
| } // end wrapHeaders | |
| bindEvents() | |
| { | |
| if ( this.bound ) | |
| return false | |
| document.addEventListener( 'click', ( e ) => | |
| { | |
| this.onClick( e ) | |
| } ) | |
| this.bound = true | |
| } // end bindEvents | |
| onClick( e ) | |
| { | |
| const t = e.target.closest( 'div.toc.wrap, #toc-ol li' ) | |
| if ( ! t ) | |
| return false | |
| e.preventDefault() | |
| e.stopPropagation() | |
| const wid = ( w.innerWidth < 981 ? 0 : 90 ) + this.delta | |
| let elem = null | |
| if ( t.classList.contains( 'wrap' ) ) | |
| elem = document.getElementById( 'toc' ) | |
| else | |
| { | |
| const lis = Array.from( document.querySelectorAll( '#toc-ol li' ) ) | |
| const index = lis.indexOf( t ) | |
| const headers = document.querySelectorAll( '.toc-header' ) | |
| if ( index < 0 || ! headers[ index ] ) | |
| return false | |
| elem = headers[ index ] | |
| } | |
| if ( ! elem ) | |
| return false | |
| const top = elem.getBoundingClientRect().top + ( w.pageYOffset || 0 ) - wid | |
| this.animateScroll( top, this.duration ) | |
| } // end onClick | |
| animateScroll( targetY, duration ) | |
| { | |
| const startY = w.pageYOffset || document.documentElement.scrollTop || 0 | |
| const diff = targetY - startY | |
| if ( diff === 0 ) | |
| return false | |
| const now = ( w.performance && typeof w.performance.now === 'function' ) ? () => w.performance.now() : () => Date.now() | |
| const start = now() | |
| const swing = ( t ) => { return 0.5 - Math.cos( Math.PI * t ) / 2 } | |
| const step = ( tNow ) => | |
| { | |
| const elapsed = tNow - start | |
| const t = Math.min( 1, elapsed / duration ) | |
| const eased = swing( t ) | |
| w.scrollTo( 0, startY + ( diff * eased ) ) | |
| if ( t < 1 ) | |
| w.requestAnimationFrame( step ) | |
| } | |
| w.requestAnimationFrame( () => | |
| { | |
| w.requestAnimationFrame( step ) | |
| } ) | |
| } // end animateScroll | |
| } // end class wwlTOC |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment