I'm trying to ditch jQuery from my upcoming projects. I found a way to create a sticky nav on scroll with pure Vanilla JS, and wanted the links on my navigation to change their active state as they reach the corresponding sections.
Below is the working solution I came up with, but I'm sure that the code can be improved to work programmatically without having to select and create conditions for each element.
I'm also using a lot of different selectors, and I'm pretty sure there must be a way to improve that, maybe using querySelectorAll and adding an eventListener in a forEach loop, but I can't get that to work either. Thanks for your help!
// select links
const allLinks = document.querySelectorAll('header nav ul li');
const linkTop = document.querySelector('#linkTop');
const linkAbout = document.querySelector('#linkAbout');
const linkServices = document.querySelector('#linkServices');
const linkClients = document.querySelector('#linkClients');
const linkContact = document.querySelector('#linkContact');
// select sections
const sectionTop = document.querySelector('#top');
const sectionAbout = document.querySelector('#about');
const sectionServices = document.querySelector('#services');
const sectionClients = document.querySelector('#clients');
const sectionContact = document.querySelector('#contact');
function changeLinkState() {
// home
if (window.scrollY >= sectionTop.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkTop.classList.add('active');
}
// about
if (window.scrollY >= sectionAbout.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkAbout.classList.add('active');
}
// services
if (window.scrollY >= sectionServices.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkServices.classList.add('active');
}
clients
if (window.scrollY >= sectionClients.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkClients.classList.add('active');
}
contact
if (window.scrollY >= sectionContact.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkContact.classList.add('active');
}
}
window.addEventListener('scroll', changeLinkState);
<nav>
<ul>
<li id="linkTop">
<a href="#top">Home</a>
</li>
<li id="linkAbout">
<a href="#about">About Us</a>
</li>
<li id="linkServices">
<a href="#services">Services</a>
</li>
<li id="linkClients">
<a href="#clients">Clients</a>
</li>
<li id="linkContact">
<a href="#contact">Contact</a>
</li>
</ul>
</nav>
Thanks a lot!
You can get all sections and links using document.querySelectorAll(). On scroll iterate the list of section from last to first, until you find one that matches. Then remove the .active class from all links, and add it to the link at the active index.
Note: You should use throttling to prevent the calling of changeLinkState multiple times in a second. Another option is to use the Intersection Observer API.
const links = document.querySelectorAll('.links');
const sections = document.querySelectorAll('section');
function changeLinkState() {
let index = sections.length;
while(--index && window.scrollY + 50 < sections[index].offsetTop) {}
links.forEach((link) => link.classList.remove('active'));
links[index].classList.add('active');
}
changeLinkState();
window.addEventListener('scroll', changeLinkState);
nav {
position: fixed;
top: 0;
right: 0;
width: 10em;
}
section {
height: 100vh;
margin: 1em 0;
background: gold;
}
.active {
background: silver;
}
<nav>
<ul>
<li id="linkTop" class="links">
<a href="#top">Home</a>
</li>
<li id="linkAbout" class="links">
<a href="#about">About Us</a>
</li>
<li id="linkServices" class="links">
<a href="#services">Services</a>
</li>
<li id="linkClients" class="links">
<a href="#clients">Clients</a>
</li>
<li id="linkContact" class="links">
<a href="#contact">Contact</a>
</li>
</ul>
</nav>
<section>
Home
</section>
<section>
About Us
</section>
<section>
Services
</section>
<section>
Clients
</section>
<section>
Contact
</section>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With