简体   繁体   中英

Click a nav item, add a class, remove class from other nav items - vanilla JS

I'm able to add a class of is-active to a nav item when it's clicked. However, i'd like to remove the class and add it to another nav item when another is clicked.

Here's what I'm currently working with:

JS:

const links = document.querySelectorAll('a');

links.forEach(function(link, index){
  link.addEventListener('click', function() {
    if(this.classList.contains('is-active')) {
      this.classList.remove('is-active');
    } else {
      this.classList.add('is-active');
    }
  });
});

Here's a Codepen example.

This attempt adds the class, but doesn't remove it when another link is clicked.

How would I remove the class? Thanks in advance.

You just loop through the links that aren't this :

const links = document.querySelectorAll('a');

links.forEach(function(link, index){
  link.addEventListener('click', function() {
    if(this.classList.contains('is-active')) {
      this.classList.remove('is-active');
    } else {
      this.classList.add('is-active');
      links.forEach(l => {                     // ***
          if (l !== this) {                    // ***
              l.classList.remove('is-active'); // ***
          }                                    // ***
      });
    }
  });
});

(See below for the for-of version.)

Alternately, you can do a new query of just the is-active links:

document.querySelectorAll('a').forEach(function(link, index){
  link.addEventListener('click', function() {
    if(this.classList.contains('is-active')) {
      this.classList.remove('is-active');
    } else {
      document.querySelectorAll('a.is-active').forEach(activeLink => { // ***
          activeLink.classList.remove('is-active');                    // ***
      });                                                              // ***
      this.classList.add('is-active');
    }
  });
});

Or if you like, since there should be only one, querySelector :

document.querySelectorAll('a').forEach(function(link, index){
  link.addEventListener('click', function() {
    if(this.classList.contains('is-active')) {
      this.classList.remove('is-active');
    } else {
      const activeLink = document.querySelector('a.is-active'); // **
      if (activeLink) {                                         // **
          activeLink.classList.remove('is-active');             // **
      }                                                         // **
      this.classList.add('is-active');
    }
  });
});

Side note: The NodeList from querySelectorAll doesn't have forEach in some browsers (it was added relatively recently). See this answer for how to add it if it's missing, and (on ES2015+ platforms) how to ensure it's iterable as well (as it's also meant to be).


And if you can rely on iterability, here are for-of versions of those:

for-of version of the first example:

const links = document.querySelectorAll('a');
for (const link of links) {
  link.addEventListener('click', function() {
    if(this.classList.contains('is-active')) {
      this.classList.remove('is-active');
    } else {
      this.classList.add('is-active');
      for (const l of links) {
          if (l !== this) {
              l.classList.remove('is-active');
          }
      }
    }
  });
}

for-of version of the second example:

for (const link of document.querySelectorAll('a')) {
  link.addEventListener('click', function() {
    if(this.classList.contains('is-active')) {
      this.classList.remove('is-active');
    } else {
      for (const activeLink of document.querySelectorAll('a.is-active')) {
          activeLink.classList.remove('is-active');
      }
      this.classList.add('is-active');
    }
  });
}

And the third:

for (const link of document.querySelectorAll('a')) {
  link.addEventListener('click', function() {
    if(this.classList.contains('is-active')) {
      this.classList.remove('is-active');
    } else {
      const activeLink = document.querySelector('a.is-active'); // **
      if (activeLink) {                                         // **
          activeLink.classList.remove('is-active');             // **
      }                                                         // **
      this.classList.add('is-active');
    }
  });
}

A common requirement - you can do this:

  • get an iterable representation of the DOM Elements using spread syntax like [...document.querySelectorAll('a')]

  • use forEach to loop through the links

  • you can use the classList.toggle function instead of having the if-else conditions

See demo below:

 // get an iterable representation using the spread syntax const elements = [...document.querySelectorAll('a')]; elements.forEach(e => e.addEventListener('click', () => { // remove active class from all links elements.forEach(e => e.classList.remove('is-active')); // add active to the clicked link e.classList.toggle('is-active'); })); 
 .is-active { color: red; } 
 <a href="#">Link 1</a> <a href="#">Link 2</a> <a href="#">Link 3</a> 

this in the function refers to the elements that is clicked. You should hide all the elements every time using forEach in each click. And then show the desired one

const links = [...document.querySelectorAll('a')];

links.forEach(function(link, index){
  link.addEventListener('click', function() {
    let temp = this.classList.contains('is-active')
    links.forEach(x => x.classList.remove('is-active'))
    if(!temp) {
      this.classList.add('is-active');
    } 
  });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM