Add Class Active On Scroll. Vanilla Js
I'm new to vanilla js. I have a navbar with links to sections. I want to make the class active as soon as the section becomes active. If there are no active section, then remove th
Solution 1:
The most minimal change you can make using the current design to implement the desired functionality is testing the section's height to ensure it's visible instead of unconditionally adding the active class to the nearest navigation link as in the current code.
if (window.scrollY - sections[index].offsetHeight <
sections[index].offsetTop) {
links[index].classList.add('active');
}
Instead of:
links[index].classList.add('active');
You can tweak the cutoff point with an offset like scrollY + 50
but hardcoding the number here seems non-ideal.
Full code:
const links = document.querySelectorAll('.nav-link');
const sections = document.querySelectorAll('.forJS');
functionchangeLinkState() {
let index = sections.length;
while (--index && window.scrollY + 50 < sections[index].offsetTop) {}
links.forEach((link) => link.classList.remove('active'));
// add the active class if within visible height of the elementif (scrollY - sections[index].offsetHeight <
sections[index].offsetTop) {
links[index].classList.add('active');
}
}
changeLinkState();
window.addEventListener('scroll', changeLinkState);
section {
height: 100vh;
}
.nav-link.active {
color: red;
}
section {
border: 1px solid #555;
}
<scriptsrc="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.min.js"></script><linkhref="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css"rel="stylesheet" /><body><headerclass="fixed-top"><navclass="navbar navbar-expand-lg navCustom"><divclass="container"><ulclass="navbar-nav justify-content-center"><liclass="nav-item"><aclass="nav-link"href="#main">Main</a></li><liclass="nav-item"><aclass="nav-link"href="#about">About us</a></li><liclass="nav-item"><aclass="nav-link"href="#portfolio">Portfolio</a></li><liclass="nav-item"><aclass="nav-link"href="#contacts">Contacts</a></li></ul></div></nav></header><sectionclass="forJS text-center">Some info 1</section><sectionclass="forJS text-center">Some info 2</section><sectionclass="forJS text-center">Some info 3</section><sectionclass="text-center">Some info 4</section><sectionclass="text-center">Some info 5</section><sectionclass="text-center">Some info 6</section><sectionclass="text-center">Some info 7</section><sectionclass="text-center">Some info 8</section><sectionclass="text-center">Some info 9</section><sectionclass="forJS text-center">Some info 10</section></body>
Your other questions were addressed in the comments but I'll reiterate the answers here:
- No parentheses are used on
changeLinkState
because we're passing the function object itself to the callback to be invoked later. If we invoked it likechangeLinkState()
, we'd wind up passingundefined
into the callback and prematurely firing the handler, as explained here. while
is empty because its block that manipulates the termination condition (i.e.--index
) is merged into the condition as shorthand, as described here.
Beyond that, there are multiple issues with the design I'll remark on briefly and leave as an exercise to the reader:
- The bootstrap layout spreads the sidebar header across the whole page, so there's probably unintentional overlap between the header and the element. If the header had a background, content would be occluded. I'd revisit the structure here to ensure multiple, non-overlapping columns or a flow layout was used.
<section>
tags should be in a parent container.- CSS properties shouldn't be
camelCased
.forJS
is not a particularly clear class name. scroll-y:auto;
is an invalid CSS property. Perhaps you meantoverflow-y: auto;
.- The strategy of firing the scroll event listener and iterating the sections is somewhat primitive. Check out throttling and consider refactoring to use an Intersection Observer.
Post a Comment for "Add Class Active On Scroll. Vanilla Js"