开发者

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.

  1. You move node 0
  2. What was node 1 becomes node 0
  3. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜