|
1 | | -const navBarHeight = document.querySelector('.header').offsetHeight; |
2 | | -document.documentElement.style.setProperty('--scroll-padding', navBarHeight - 1 + "px") |
| 1 | +const navBarHeight = document.querySelector(".header").offsetHeight; |
| 2 | +document.documentElement.style.setProperty( |
| 3 | + "--scroll-padding", |
| 4 | + navBarHeight - 1 + "px" |
| 5 | +); |
3 | 6 |
|
4 | | -const headerNav = document.querySelector('.header-nav'); |
5 | | -const navToggle = document.querySelector('.mobile-nav-toggle'); |
| 7 | +const headerNav = document.querySelector(".header-nav"); |
| 8 | +const navToggle = document.querySelector(".mobile-nav-toggle"); |
6 | 9 |
|
7 | 10 | // Get all sections that have an ID defined |
8 | 11 | const sections = document.querySelectorAll("section[id]"); |
9 | 12 | // Add an event listener listening for scroll |
10 | 13 | window.addEventListener("scroll", () => navHighlighter(navBarHeight)); |
11 | 14 |
|
12 | | -navToggle.addEventListener('click', navButtonToggler); |
13 | | - |
14 | | - |
| 15 | +navToggle.addEventListener("click", navButtonToggler); |
15 | 16 |
|
16 | 17 | function navButtonToggler() { |
17 | | - const visibility = headerNav.getAttribute('data-visible'); |
| 18 | + const visibility = headerNav.getAttribute("data-visible"); |
18 | 19 |
|
19 | 20 | if (visibility === "false") { |
20 | | - headerNav.setAttribute('data-visible', "true"); |
21 | | - navToggle.setAttribute('aria-expanded', "true"); |
| 21 | + headerNav.setAttribute("data-visible", "true"); |
| 22 | + navToggle.setAttribute("aria-expanded", "true"); |
22 | 23 | } else { |
23 | | - headerNav.setAttribute('data-visible', "false"); |
24 | | - navToggle.setAttribute('aria-expanded', "false"); |
| 24 | + headerNav.setAttribute("data-visible", "false"); |
| 25 | + navToggle.setAttribute("aria-expanded", "false"); |
25 | 26 | } |
26 | 27 | } |
27 | 28 |
|
28 | | -const getOffsetTop = element => { |
| 29 | +const getOffsetTop = (element) => { |
29 | 30 | let offsetTop = 0; |
30 | 31 | while (element) { |
31 | 32 | offsetTop += element.offsetTop; |
32 | 33 | element = element.offsetParent; |
33 | 34 | } |
34 | 35 | return offsetTop; |
| 36 | +}; |
| 37 | + |
| 38 | +const bottomVisible = () => |
| 39 | + document.documentElement.clientHeight + window.scrollY >= |
| 40 | + (document.documentElement.scrollHeight || |
| 41 | + document.documentElement.clientHeight); |
| 42 | + |
| 43 | +// 'getViewPercentage' was adapted from https://gist.github.com/rijkvanzanten/df73ae28e80b9c6e5030baed4d1a90a6 |
| 44 | +// Thanks to https://gist.github.com/rijkvanzanten |
| 45 | +function getViewPercentage(element) { |
| 46 | + const viewport = { |
| 47 | + top: window.scrollY, |
| 48 | + bottom: window.scrollY + window.innerHeight, |
| 49 | + }; |
| 50 | + |
| 51 | + const elementBoundingRect = element.getBoundingClientRect(); |
| 52 | + const elementPos = { |
| 53 | + top: elementBoundingRect.y + window.scrollY, |
| 54 | + bottom: elementBoundingRect.y + elementBoundingRect.height + window.scrollY, |
| 55 | + }; |
| 56 | + |
| 57 | + if (viewport.top > elementPos.bottom || viewport.bottom < elementPos.top) { |
| 58 | + return 0; |
| 59 | + } |
| 60 | + |
| 61 | + // Element is fully within viewport |
| 62 | + if (viewport.top < elementPos.top && viewport.bottom > elementPos.bottom) { |
| 63 | + return 100; |
| 64 | + } |
| 65 | + |
| 66 | + // Element is bigger than the viewport |
| 67 | + if (elementPos.top < viewport.top && elementPos.bottom > viewport.bottom) { |
| 68 | + return 100; |
| 69 | + } |
| 70 | + |
| 71 | + const elementHeight = elementBoundingRect.height; |
| 72 | + let elementHeightInView = elementHeight; |
| 73 | + |
| 74 | + if (elementPos.top < viewport.top) { |
| 75 | + elementHeightInView = elementHeight - (window.scrollY - elementPos.top); |
| 76 | + } |
| 77 | + |
| 78 | + if (elementPos.bottom > viewport.bottom) { |
| 79 | + elementHeightInView = |
| 80 | + elementHeightInView - (elementPos.bottom - viewport.bottom); |
| 81 | + } |
| 82 | + |
| 83 | + const percentageInView = (elementHeightInView / window.innerHeight) * 100; |
| 84 | + |
| 85 | + return Math.round(percentageInView); |
35 | 86 | } |
36 | 87 |
|
37 | | -function navHighlighter(correction = 100) { |
| 88 | +function navHighlighter( |
| 89 | + correction = 100, |
| 90 | + min_percent_accept = 20, |
| 91 | + master_percent = 75 |
| 92 | +) { |
38 | 93 | // Get current scroll position |
39 | 94 | let Yscroll = window.scrollY; |
40 | 95 |
|
41 | 96 | // Now we loop through sections to get height, top and ID values for each |
42 | | - sections.forEach(current => { |
| 97 | + sections.forEach((current) => { |
43 | 98 | const sectionHeight = current.offsetHeight; |
44 | 99 | const sectionTop = getOffsetTop(current) - correction; |
45 | 100 | let sectionId = current.getAttribute("id"); |
| 101 | + let viewportPercentage = getViewPercentage(current); |
46 | 102 |
|
47 | 103 | /* |
48 | | - - If our current scroll position enters the space where current section on screen is, add .active class to corresponding navigation link, else remove it |
49 | | - - To know which link needs an active class, we use sectionId variable we are getting while looping through sections as an selector |
50 | | - */ |
| 104 | + - If our current scroll position enters the space where current section on screen is |
| 105 | + and the section is at least 'min_percent_accept' in view |
| 106 | + or the current section is more than 'master_percent' in view, add .active class to corresponding navigation link, else remove it |
| 107 | + - To know which link needs an active class, we use sectionId variable we are getting while looping through sections as an selector |
| 108 | + */ |
51 | 109 | if ( |
52 | | - Yscroll > sectionTop && |
53 | | - Yscroll <= sectionTop + sectionHeight |
| 110 | + (Yscroll > sectionTop && |
| 111 | + Yscroll <= sectionTop + sectionHeight && |
| 112 | + viewportPercentage > min_percent_accept) || |
| 113 | + viewportPercentage > master_percent |
54 | 114 | ) { |
55 | | - document.querySelector("nav a[href*=" + sectionId + "]").setAttribute("active", "true"); |
56 | | - |
| 115 | + document |
| 116 | + .querySelector("nav a[href*=" + sectionId + "]") |
| 117 | + .setAttribute("active", "true"); |
57 | 118 | } else { |
58 | | - document.querySelector("nav a[href*=" + sectionId + "]").setAttribute("active", "false"); |
| 119 | + document |
| 120 | + .querySelector("nav a[href*=" + sectionId + "]") |
| 121 | + .setAttribute("active", "false"); |
59 | 122 | } |
60 | 123 | }); |
61 | 124 | } |
0 commit comments