Skip to content

Instantly share code, notes, and snippets.

@gerrgg
Created November 4, 2021 02:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gerrgg/5039f7824173318ad8e2030d9e3d1847 to your computer and use it in GitHub Desktop.
Save gerrgg/5039f7824173318ad8e2030d9e3d1847 to your computer and use it in GitHub Desktop.
const handleHeaderLogic = (() => {
const header = document.querySelector('header');
const sections = document.querySelectorAll('.section');
const whereSectionsStartAndEnd = {}
const headerHeight = 132;
let lastPosition = Math.ceil(window.scrollY);
let ticking = false;
// scrolls to section when menu item is clicked
const handleClickOnMenuItems = (() => {
const menuItems = document.querySelectorAll('li.menu-item:not(.download)');
for (let link of menuItems) {
link.addEventListener('click', (e) => {
console.log('click');
e.preventDefault()
const target = e.target.parentElement.classList[0]
if (target) {
history.pushState(null, null, `#${target}`)
start = whereSectionsStartAndEnd[`${target}`]
? whereSectionsStartAndEnd[`${target}`].start + 1
: 0
jQuery('body,html').animate({ scrollTop: start }, 500);
}
})
}
})()
// https://gomakethings.com/how-to-get-an-elements-distance-from-the-top-of-the-page-with-vanilla-javascript/
const getOffsetTop = (elem) => {
// Set our distance placeholder
let distance = 0;
// Loop up the DOM
if (elem.offsetParent) {
do {
distance += elem.offsetTop;
elem = elem.offsetParent;
} while (elem);
}
// Return our distance
return distance < 0 ? 0 : distance - headerHeight;
};
/**
* When the page is scrolled, loop the start/end positions object and check which section
* we are in, then calculate the percentage of how far we've scrolled into the section.
* pass menuItem, length of section and position within section to handler.
* @param {int} position
*/
const findCurrentSectionAndDetermineLinePosition = (position) => {
// loop sections object
for (const [id, section] of Object.entries(whereSectionsStartAndEnd)) {
// if at bottom of page, just set contact to active and put line at end
if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
const menuItem = document.querySelector(`li.contact`);
handleActiveStateAndLinePosition(menuItem, 1, 1);
// else if position is within a section and not at bottom of page
} else if (position >= section.start && position <= section.end) {
// get the menu item
const menuItem = document.querySelector(`li.${id}`);
// get length of a section and find position within that section
let length = section.end - section.start;
let p = position - section.start;
handleActiveStateAndLinePosition(menuItem, length, p);
}
}
}
/**
* Removes active state from current menu item and sets it on the new one.
* Moves orange line into position
* @param {Element} menuItem
* @param {int} length
* @param {int} p
*/
const handleActiveStateAndLinePosition = (menuItem, length, p) => {
let activeMenuItem = document.querySelector(`li.active`);
const orangeLine = menuItem.lastChild;
// if theres no active menu item, set the target menu item to active
if (!activeMenuItem) {
activeMenuItem = menuItem
}
// if menu item is not the action item, remove active state
if (menuItem && menuItem !== activeMenuItem) {
activeMenuItem.classList.remove('active')
activeMenuItem.lastChild.style.left = 'unset';
}
// if we have a menu item, add active class to it and position line
if (menuItem) {
// add active to the targeted menu item
menuItem.classList.add('active');
// position orange line
orangeLine.style.left = `${(p / length) * 100}%`
}
}
/**
* Calculates the beginning and end of each section and populates object used for determining
* active state and position withing each section
* @param {bool} init
*/
const getSectionsStartAndEnd = (init = false) => {
// give the images a moment to load in before we find the start/end of each section
setTimeout(() => {
// loop the sections
for (let section of sections) {
const end = section.nextElementSibling
// if its not the start, set the distance from the top of the page
// if theres an end, get its position from top of page else set it to height of document
const startAndEnd = {
start: (section.id !== 'strategy') ? getOffsetTop(section) : 0,
end: end ? end.offsetTop - headerHeight : document.body.clientHeight
}
whereSectionsStartAndEnd[`${section.id}`] = startAndEnd
}
// set position on init state
if (init) {
if (location.hash) {
const targetButton = document.querySelector(`li.${location.hash.substring(1)} > a`);
targetButton.click()
} else {
findCurrentSectionAndDetermineLinePosition(lastPosition)
}
}
}, 1000)
}
// Reference: http://www.html5rocks.com/en/tutorials/speed/animations/
document.addEventListener('scroll', function (e) {
lastPosition = Math.ceil(window.scrollY);
if (!ticking) {
window.requestAnimationFrame(function () {
findCurrentSectionAndDetermineLinePosition(lastPosition);
ticking = false;
});
ticking = true;
}
});
// update start and end points when screen resizes
window.addEventListener('resize', () => {
getSectionsStartAndEnd();
})
// init
getSectionsStartAndEnd(true);
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment