Created
May 13, 2021 09:36
-
-
Save geekman/d859ad8c6d7a7d7c62c3039f47fa4791 to your computer and use it in GitHub Desktop.
a minimal Scrollspy implementation in vanilla JavaScript
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
// | |
// a minimal Scrollspy implementation written in pure JS | |
// not widely tested, may have bugs | |
// | |
// 2020.05.13 darell tan | |
// | |
function scrollSync(tocSel, headingsSel) { | |
var offset = 0; | |
var tocElems = document.querySelector(tocSel).querySelectorAll('a[href^="#"]'); | |
var headings = document.querySelector(headingsSel).querySelectorAll('h1,h2,h3,h4,h5,h6'); | |
var headingsPos; | |
var matchHeading = function(a) { | |
var t = a.getAttribute('href'); | |
if (t[0] != '#') return; | |
t = t.substring(1); | |
var h = [].filter.call(headings, h => h.id == t); | |
return h ? h[0] : null; | |
}; | |
var getOffsets = function() { | |
// map tocElems => pos | |
headingsPos = [].map.call(tocElems, (e) => { | |
var h = matchHeading(e); | |
return h ? h.offsetTop : -1; | |
}); | |
// also calculate our intersection offset here | |
// as the header crosses the top 1/6th of the window, it registers | |
// for too small window sizes, an arbitrary minimum is ensured | |
offset = window.innerHeight / 6; | |
if (offset < 80) offset = 80; | |
//[].map.call(tocElems, (e, i) => console.log(e, headingsPos[i]) ); | |
} | |
var onScroll = function(e) { | |
var pos = document.documentElement.scrollTop + offset; | |
for (var i = 0; i < tocElems.length; i++) { | |
if (pos >= headingsPos[i] && ( | |
i == tocElems.length - 1 || // last elem | |
pos < headingsPos[i+1])) { | |
tocElems[i].classList.add('active'); | |
} else { | |
tocElems[i].classList.remove('active'); | |
} | |
} | |
}; | |
var onTocClick = function(ev) { | |
var e = ev.target; | |
var h = matchHeading(e); | |
if (h) { | |
toggleSidebar(); | |
// need to make sure we are still past the offset to be | |
// recognized as "within" that section | |
window.scroll({top: h.offsetTop - 0.75*offset, behavior: 'smooth'}); | |
window.history.replaceState({}, document.title, e.getAttribute('href')); | |
ev.preventDefault(); | |
return false; | |
} | |
}; | |
[].forEach.call(tocElems, (e) => { | |
e.addEventListener('click', onTocClick); | |
}); | |
// initial calls | |
getOffsets(); | |
onScroll(); | |
window.addEventListener('scroll', onScroll); | |
window.addEventListener('resize', () => { getOffsets(); onScroll(); }); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment