Created
November 4, 2021 02:18
-
-
Save gerrgg/5039f7824173318ad8e2030d9e3d1847 to your computer and use it in GitHub Desktop.
This file contains 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
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