error with insertBefore, not all elements are inserted
If you fiddle with tag string, 开发者_运维知识库for example by putting space between ><
, the effect is different.
<!DOCTYPE html>
<head><title>insertBefore error</title></head>
<body>
<div id='myElement'>above this div 3 elements should be inserted
but actually only 2 are, text2 is missing</div>
<script type="text/javascript">
referenceTag = document.getElementById('myElement');
var newElement = document.createElement('div');
newElement.innerHTML = '<div>text1</div><pre>text2</pre><p>text3</p>';
for (i=0; i<newElement.childNodes.length; i++) {
alert(newElement.childNodes[i].tagName);
//if you comment-out following line there are 3 alerts but otherwise only two.
referenceTag.parentNode.insertBefore(newElement.childNodes[i],referenceTag);
}
</script>
</body>
When you call insertBefore
you move the node so it becomes a child of referenceTag.parentNode
. At this point it is no longer a child of newElement
so newElement.childNodes
shrinks by one.
You then increment i
, so you keep skipping elements.
i.e.
- You move node 0
- What was node 1 becomes node 0
- You move the new node 1 and leave the old node 1 (and current node 0) behind.
You probably want:
while (newElement.firstChild) {
alert(newElement.firstChild.tagName);
referenceTag.parentNode.insertBefore(newElement.firstChild,referenceTag);
}
If you fiddle with tag string, for example by putting space between ><, the effect is different.
The effect is actually the same, but harder to see. Inserting spaces means that instead of having a data structure along the lines of:
[ elementNode, elementNode, elementNode ]
You have:
[ elementNode, textNode, elementNode, textNode, elementNode ]
Since you skip every other node, and every other node is a text node consisting entirely of white space, you can't see the effect.
When you insert the nodes with:
referenceTag.parentNode.insertBefore(newElement.childNodes[i],referenceTag);
The length of newElement.childNodes.length
is always reduced by 1. Additionally, the index of the elements change when you remove elements before others, so you should be storing the length for the for loop and always use the index 0:
referenceTag = document.getElementById('myElement');
var newElement = document.createElement('div');
newElement.innerHTML = '<div>text1</div><pre>text2</pre><p>text3</p>';
var storeLength = newElement.childNodes.length
for (i=0; i<storeLength; i++) {
alert(newElement.childNodes[0].tagName);
//if you comment-out following line there are 3 alerts but otherwise only two.
referenceTag.parentNode.insertBefore(newElement.childNodes[0],referenceTag);
}
example: http://jsfiddle.net/djttp/5/
The problem is that newElement.childNodes
is a live collection of nodes. So whenever you access a property of it, the collection is reevaluated, updated to reflect the current status of the DOM.
Example:
First Iteration
i = 0
newElement.childNodes[0] == '<div>'
newElement.childNodes[1] == '<pre>'
newElement.childNodes[2] == '<p>'
newElement.childNodes.length == 3
The <div>
element is prepended.
Second iteration:
i = 1
newElement.childNodes[0] == '<pre>'
newElement.childNodes[1] == '<p>'
newElement.childNodes.length == 2
The <p>
element is prepended.
Third iteration:
i = 2
newElement.childNodes[0] == '<pre>'
newElement.childNodes.length == 1
The loop stops because the condition is not fulfilled.
@Quentin provided a good solution for this problem.
精彩评论