Search code examples
javascriptfor-loopgetelementsbyclassname

How do I remove instances of classes that do not match a dropdown value using a for loop?


I am trying to implement filtering on my page using a dropdown, and the removal of classes that do not match the drop down selection by comparing items using a loop. However, when I execute the code, a seemingly random selection of items get removed. Maybe it's not random, but I can't figure out how the removed items are being selected. I'm sure there's something wrong with the way I wrote the code, but I'm just not sure where I'm going wrong.

When I log the variables to the console, they appear as expected.

const filter = document.querySelector('.mainFilter');

if (filter) {
  var testing = document.getElementsByClassName('bigMomma');
  var testing2 = testing[0].getElementsByClassName('innerArticle');
  console.log(testing2[1].className);
  filter.addEventListener('change', () => {
    var filterValue = filter.value;
    var classNameCompare = `innerArticle ${filterValue}`;
    console.log(classNameCompare);
    for (let i = 0; i < testing2.length; i++) {
      if (testing2[i].className === classNameCompare) {
        console.log(`All good.`);
      } else {
        testing2[i].remove();
      }
    }
  });
}

Solution

  • getElementsByClassName returns an unintuitive live collection which contains all elements in the document that match the class at that instant that the collection is examined. So if you .remove() one of the classes from the DOM while you're iterating over the collection, the collection's indicies will rearrange themselves to fill in the hole created by the removed element. For example:

    const coll = document.getElementsByClassName('foo');
    for (let i = 0; i < coll.length; i++) {
      coll[i].remove();
    }
    <div class="foo">foo</div>
    <div class="foo">foo</div>

    As you can see above, the 2nd element still exists in the DOM because when the 1st was removed, the indicies rearranged themselves, so on the next iteration, when i was 1, the loop stopped.

    Easiest solution is to use querySelectorAll instead, which returns a static NodeList that won't mutate itself while you're iterating over it. Change:

    var testing = document.getElementsByClassName('bigMomma');
    var testing2 = testing[0].getElementsByClassName('innerArticle');
    

    to

    const innerArticles = document.querySelectorAll('.bigMomma .innerArticle');
    

    and then reference innerArticles and .remove() elements of it when needed.