2

I want to get all elements which belong to two specific different classes and remove and add these classes seperately from these elements. I tried:

.concealed {
  display: none;
}
.slide {
  background: #333;
  color: #fff;
}

// <div class="slide concealed">myText</div><br> <div class="slide"><div class="concealed">myText</div></div><br> <div class="slide"><div class="concealed">myText_2</div></div><br> <div class="slide"><div class="concealed">myText_3</div></div><br>

    // var slides = document.getElementsByClassName('slide');
    // var slides = document.querySelectorAll('.slide, .concealed');
       var slides = document.getElementsByClassName('slide concealed');

slides[0].classList.remove("concealed");

As you can see I tried several ways to achieve this but I can only remove and add "concealed" when I do var slides = document.getElementsByClassName('concealed'); . When doing getElementsByClassName with multiple classnames it seems to miss out concealed and only get slide. E.g. doing slides[0].classList.remove("concealed"); after document.getElementsByClassName('slide concealed'); has no effect.

I am sure I am missing something, this can't that hard to implement. Any ideas? Thanks.

2
  • "...and remove and add these classes seperately from these elements..." I'm afraid I don't quite understand what you're trying to do with the elements. Your commented-out code using querySelectorAll should work, but it seems like it didn't. Can you give us more information on that? Commented Sep 15, 2019 at 16:21
  • after trying var slides = document.querySelectorAll('.slide, .concealed'); I noticed that slides.lengthwas the double count of my elements and assumed that this can't be right. it seems i need to read about NodeList handling ;) Commented Sep 15, 2019 at 16:32

1 Answer 1

1

The issue is that getElementsByClassName is a live HTMLCollection. When an element no longer matches (because you've removed the class), it's removed from the collection:

const list = document.getElementsByClassName("example");
console.log(list.length);       // 2
console.log(list[0].innerHTML); // "A"
list[0].classList.remove("example");
console.log(list.length);       // 1
console.log(list[0].innerHTML); // "B"
<div class="example">A</div>
<div class="example">B</div>

This means that if you're doing a loop and operate on the first entry, then increment your index, you'll miss what used to be the second entry because it's the first entry now.

A couple of ways to fix that:

  1. Loop backward, or
  2. Convert the HTMLCollection into an array before starting, or
  3. Use querySelectorAll to get a snapshot NodeList instead, so that the lists contents don't change while you're updating.

Here's an example removing the classes red and bold from a series of elements using querySelectorAll so the NodeList is static:

setTimeout(() => {
    const list = document.querySelectorAll(".red, .bold");
    for (let n = 0; n < list.length; ++n) {
        list[n].classList.remove("red");
        list[n].classList.remove("bold");
    }
}, 800);
.red {
    color: red;
}
.bold {
    font-weight: bold;
}
<div class="red">red 1</div>
<div class="bold">bold 1</div>
<div class="red bold">red 2 and bold 2</div>
<div class="bold">bold 3</div>
<div class="bold">bold 4</div>


See also my answer here: NodeList is now officially iterable, meaning you should be able to use a for-of loop on it. That answer shows how to polyfill that on slightly-older environments that haven't implemented it yet. It also shows how to add iterability to HTMLCollection (but note that HTMLCollection is not specified to be iterable).

Sign up to request clarification or add additional context in comments.

1 Comment

The problem was indeed that getElementsByClassName is a live collection and not a snapshot. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.