mitomed
mitomed

Reputation: 2066

Javascript for loop doesn't exit and keeps updating getElementsByTagName variable

I have this simple javascript snippet

function foo() {
  bar();
}

function bar() {
  var tags = document.getElementsByTagName('example');
  
  for (var i = 0; i < tags.length; i++) {
    console.log(tags.length);
    tags[i].innerHTML = '<example2>Inserted</example2>';
  }
}

foo();
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.2/lodash.min.js"></script>
<example></example>

The thing I don't get is that when I change the inserted innerHTML to be Inserted it doesn't exit the loop as it keeps "updating" the variable tag (I added the 2 in the snippet to avoid it if you run it);

As a side note, if I use the lodash forEach it does what I expected to do with the for loop, just runs the loop code once.

function foo() {
  bar();
}

function bar() {
  var tags = document.getElementsByTagName('example');

  _.forEach(tags, function(value) {
    console.log(tags.length);
    value.innerHTML = '<example>Inserted</example>';
  });
}

foo();
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.2/lodash.min.js"></script>
<example></example>

I can't understand why the loop keeps udpdating the variable with the nested tags.

Thanks

Upvotes: 0

Views: 103

Answers (1)

Rob M.
Rob M.

Reputation: 36521

document.getElementsByTagName and its sibling DOM methods return a live HTMLCollection of elements, meaning that if the new elements that match your query are added, the variable containing the reference to the HTMLCollection will be updated. You should either:

a) Use Array.from to "detach" the collection:

var tags = Array.from(document.getElementsByTagName('element'));

Or if you don't have Array.from available, you could use Array.prototype.slice:

var tags = Array.prototype.slice.call(document.getElementsByTagName('a'), 0)

b) Not add elements that match the query you are iterating over (which is creating an infinite loop)


This is also why it's suggested that if you are iterating over a collection of elements in a for loop, you capture the length of the list prior to iteration and use it as the loop boundary:

var tags = document.getElementsByTagName('example');
var length = tags.length;

for (var i = 0; i < length; i++) {
   // won't be infinite
   document.createElement('example');
   ...
}

Upvotes: 3

Related Questions