Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
338 views
in Technique[技术] by (71.8m points)

javascript - Vanilla JS change active state of links when scrolling refactoring

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>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...