Using removeChild with HTMLCollection

Ad

Consider this code:

xr = document.querySelectorAll('.material-tooltip'); // NodeList
console.log(xr.length); // 50
for (ya of xr)
  ya.parentNode.removeChild(ya);
zu = document.querySelectorAll('.material-tooltip');
console.log(zu.length); // 0

This works as expected, it removes all of the found elements. Now consider this code:

xr = document.getElementsByClassName('material-tooltip'); // HTMLCollection
console.log(xr.length); // 50
for (ya of xr)
  ya.parentNode.removeChild(ya);
zu = document.getElementsByClassName('material-tooltip');
console.log(zu.length); // 25

It is only removing half of the found elements. What is causing this?

Ad

Answer

Ad

querySelectorAll returns a non-live NodeList. getElementsByClassName returns a live HTMLCollection. The former behaves like any array: you go through the array, then do something for each element. But a live HTMLCollection is different - its contents reflect the current situation in the DOM at any time; if you remove an element from DOM, it disappears in xr, and if you add an element to DOM that fits the selector, it will appear in xr, even after you run getElementsByClassName.

Let's do the first iteration of ya being 0. You remove xr[0], and it disappears from the list. Now the element 1 is xr[0]; ya becomes 1; and you remove xr[1] (element 2), with the element 1 being skipped. You then remove xr[2] (element 3), and skip element 4... etc.

Whenever you are operating on a live HTMLCollection, either go from back to front so that disappearing elements don't mess you up, or clone the HTMLCollection to fix its elements in place, or just do a loop removing xr[0] until xr is empty.

Ad
source: stackoverflow.com
Ad