The mischief 'children' of Javascript

The mischief 'children' of Javascript

Here we are going to discuss the quirky behavior of the children property in javascript. The quirkiness is that for loop not working properly in the case of parent.children or parent.childNodes property as they are live lists. So while removing the elements, the iterator, therefore, isn't guaranteed to iterate over all the elements.

Let's dive deep into it with an example πŸ‘‡

Here's the HTML πŸ‘‡

<ul id="parent">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
  <li>Four</li>
  <li>Five</li>
  <li>Six</li>
</ul>

Now, let's say I want to remove the elements in odd positions like the first element(one), the third element(three), and the fifth element(five).

So, we can easily do it by targeting the parent id and removing the required elements using the remove() method by looping through them.

let elements = document.querySelectorAll("#parent li");
for(let i = 0; i < elements.length; i++) {

    // Removing the even index values
    if(i % 2 === 0) {
        elements[i].remove();
    }
}

basic for.png

We can also do it using the forEach() method.

let elements = document.querySelectorAll("#parent li");
elements.forEach((element, index) => {
    if(index % 2 === 0) {
        element.remove();
    }
});

Now, as we know that the <li> elements are children of parent <ul> so using the children property we should also be able to perform this operation.

let elements = document.querySelector("#parent").children;
for(let i = 0; i < elements.length; i++) {
    if(i % 2 === 0) {
        elements[i].remove();
    }
}

Here's the result πŸ‘‡

Isn't it shocking?😨

This is the behavior I was talking about.

Let me give you another example.

Let's say we want to remove all the elements. So, generally, what will we do? It will be the same as above except the condition i % 2 === 0.

let elements = document.querySelector("#parent").children;
for(let i = 0; i < elements.length; i++) {
  elements[i].remove();  
}

Let's see the result πŸ‘‡

Isn't it more shocking?😁

This is the result we desired in the previous case right?

When we wanted to have this same output it was giving a different result and now it is giving that output when we wanted to eliminate all the elements by removing the condition.

It may seem to be overwhelming and confusingπŸ€”

Don't worry! Let me explain how the looping works in case of removing all the elements with a diagram πŸ‘‡

children-explanation.png

parent.children and parent.childNodes are live lists which means it instantly modifies the list if any operation is performed. Here, the moment remove() method is called, the element at the given index is getting removed and the list is getting modified resulting in the shifting of elements at the consecutive indexes down. For this reason, the iterator whose value increased is unable to catch the very next element as its index gets changed.

  • In the first step, the element(One) at index value 0 is getting removed as i = 0 and all the other elements are getting shifted one place down.
  • In the second step, the iterator's value got increased by 1 (i = 1) and the element at index value 1 is 'Three'. So 'Three' is getting removed and not 'Two' as its index value is 0 now. So 'Two' remains untouched. After the removal of 'Three' all the elements next to it are again getting shifted one place down.
  • Similarly in the third step, 'Five' is getting removed as its index value is 2 and i = 2.
  • In the last step, the loop ends as i = 3 and there is no value at the third index because 'Six' got shifted to index value 2 due to the removal of 'Five'.

This happens only if elements are getting removed from the beginning.

If we perform the same operation from the end of the collection it will work fine with no quirky behavior because there is no list modification occurring while removing the elements. Also, it's significantly faster and cheaper to remove the last element of a collection than it is to remove the first element.

// Removing elements from the end
let elements = document.querySelector("#parent").children;
for (let i = elements.length-1; i >= 0; i--) {
    if(i % 2 === 0) {
        elements[i].remove();
    }
}

That's all from my sideπŸ€—

Click here if you would like to read my other blogsπŸ“

Happy coding πŸ˜„βœ¨

Β