Why does removing an element with javascript prevent iteration of elements?
I am trying to replace all text fields on a page with labels.
function replaceInputTextFieldsWithValues() {
var inputFields = document.getElementsByTagName("input");
for(var i = 0; i < inputFields.length; i++) {
if(inputFields[i].getAttribute("type")== "text") {
var parent = inputFields[i].parentNode;
var value = inputFields[i].value;
parent开发者_Go百科.removeChild(inputFields[i]);
var label = document.createElement('label');
label.setAttribute('for', value);
label.innerHTML = value;
parent.appendChild(label);
}
}
}
My HTML document is organized in tables. This function only seems to work on the first element in each table.
On the other hand, when I remove the line:
parent.removeChild(inputFields[i]);
The code seems to work fine. Why is this happening and how do I fix it?
What you get back from getElementsByTagName
is an HTMLCollection
, which is live. (This is true for the other getElementsByXYZ
methods, but not querySelectorAll
.) That means if you remove the element at index 0
, the HTMLCollection
's length will go down and you'll have a new element at index 0
instead of the one you just removed.
Just work your way through it backward and you'll be fine:
for(var i = inputFields.length - 1; i >= 0; i--) {
// ...
}
Alternately, convert the HTMLCollection
into an array and then loop through the array. (See the live example and code below).
Edit: Or, as Chris Shouts points out in the comments, you can just make use of the changing length
, but it's not quite as simple as Chris' suggestion because you're only removing the elements sometimes. It would look like this:
var inputFields = document.getElementsByTagName("input");
var i = 0;
while (i < inputFields.length) {
if(inputFields[i].getAttribute("type")== "text") {
// Remove it and DON'T increment `index`
}
else {
// Skip this one by incrementing `index`
++index;
}
}
Which of these three approaches to use will depend on the situation. Copying to an array gives you a nice static dataset to work with, and if you make sure to release the reference to the HTMLCollection
, you're giving the browser the opportunity to realize it doesn't have to keep that list up-to-date when things change, which could reduce overhead. But you're copying the references briefly, which increases overhead a bit. :-)
Additional: Here's an example showing this effect, and also showing a fairly efficient (but obscure) way to create an array from a HTMLCollection
:
HTML:
<ul>
<li>LI0</li>
<li>LI1</li>
<li>LI2</li>
</ul>
JavaScript:
var lilist, liarray;
// Get the HTMLCollection, which is live
lilist = document.getElementsByTagName('li');
// Create an array of its elements
liarray = Array.prototype.slice.call(lilist, 0);
// Show initial length of both
display("lilist.length = " + lilist.length); // Shows 3
display("liarray.length = " + liarray.length); // Shows 3
// Show what the 0th element of both is (both show "LI0" in the live example)
display("lilist[0].innerHTML = " + lilist[0].innerHTML); // Shows LI0
display("liarray[0].innerHTML = " + liarray[0].innerHTML); // Shows LI0
// Remove the first list item
display("Removing item 0");
lilist[0].parentNode.removeChild(lilist[0]);
// Show the length of both, note that the list's length
// has gone down, but the array's hasn't
display("lilist.length = " + lilist.length); // Shows 2, not 3
display("liarray.length = " + liarray.length); // Still shows 3
// Show what the 0th element of both *now* is
display("lilist[0].innerHTML = " + lilist[0].innerHTML); // Shows LI1 now
display("liarray[0].innerHTML = " + liarray[0].innerHTML); // Still shows LI0
Live copy
精彩评论