Andrew Andrew - 4 months ago 11
Javascript Question

Unexpected output while using JavaScript for loop in the context of removing Child Nodes

Given below are my HTML and two JavaScript looping constructs (

for
and
while
). When executed, the
while
loop works as expected, but the
for
loop doesn't seem to get executed in one like the
while
loop is. The
for
loop code has to be re-run till all the child nodes are removed.

Using a
for
loop:



var article = document.getElementsByTagName("article")[0];
for (var i = 0; i < article.childNodes.length; i++) {
article.removeChild(article.childNodes[i]); //even you can replace i with 0 (zero)
}

<header>
<h1>An Introduction to JavaScript</h1>
<p>Removing Elements</p>
</header>

<article>

<p id="p1">My <strong>first</strong> paragraph.</p>

<p id="p2">My <strong>second</strong> paragraph.</p>

</article>





Using a
while
loop:



var article = document.getElementsByTagName("article")[0];
while (article.childNodes.length) {
article.removeChild(article.childNodes[0])
}

<header>
<h1>An Introduction to JavaScript</h1>
<p>Removing Elements</p>
</header>

<article>

<p id="p1">My <strong>first</strong> paragraph.</p>

<p id="p2">My <strong>second</strong> paragraph.</p>

</article>





What's going on with the
for
loop here?

Answer

If you remove an entry from the beginning of the childNodes collection, the collection immediately adjusts for the fact that child is no longer there, so the element that was at index 1 is now at index 0, index 2 moved to index 1, etc. length also updates dynamically, as you'd expect.

Since you're increasing i as you go, you end up skipping every other element: You remove 0, which moves the others down; then you remove 1, which moves the others down, etc. It happens that in your example, you end up removing the text nodes and not the elements. Here's what happens:

To start with, you have this inside your article element:

0: Text node containing whitespace
1: `p` element
2: Text node with whitespace
3: `p` element
4: Text node with whitespace

The loop with i == 0 removes the first one, leaving us with:

0: `p` element
1: Text node with whitespace
2: `p` element
3: Text node with whitespace

...and i is now 1. Then the next iteration removes the node at index 1, leaving is with:

0: `p` element
1: `p` element
2: Text node with whitespace

...and i is now 2. Then the next iteration removes the node at index 2:

0: `p` element
1: `p` element

...and i is now 3, length is now 2, and the loop stops.

To use the for loop, loop backward

for (var i = article.childNodes.length - 1; i >= 0; i--) {
    article.removeChild(article.childNodes[i]);
} 

var article = document.getElementsByTagName("article")[0];
for (var i = article.childNodes.length - 1; i >= 0; i--) {
  article.removeChild(article.childNodes[i]);
}
<header>
  <h1>An Introduction to JavaScript</h1>
  <p>Removing Elements</p>
</header>

<article>

  <p id="p1">My <strong>first</strong> paragraph.</p>

  <p id="p2">My <strong>second</strong> paragraph.</p>

</article>

See also: What is the best way to empty an node in JavaScript and its answers.