首先看下面一段代码:
<div id="box">
<p>参考的P标签元素</p>
</div>
<hr>
<div id="target">
<p>p text 1</p>
<p>p text 2</p>
<p>p text 3</p>
<p>p text 4</p>
<p>p text 5</p>
</div>
<script>
var box = document.getElementById("box");
var target = document.getElementById("target");
var targetChildrens = target.children;
for (var i = 0; i < targetChildrens.length; i++) {
box.appendChild(targetChildrens[i]);
}
</script>
我们知道使用 appendChild()
给一个父节点插入子节点,这个方法插入的位置都是在父节点元素最后一个子元素之后插入的。
所以可以看出,这段代码的意图是想把 id 为 target
下的所有 p 标签元素追加到 id 为 box
中 p 标签的后面,但需要注意的是: 如果插入的子节点是DOM文档树里面已经存在的节点的话,这个节点会被移动到用appendChild() 插入的位置,而不会被复制。
如果想复制而不是移动追加,那么就需要使用 cloneNode()
,该方法将复制并返回调用它的节点的副本。语法如下:
node.cloneNode(deep);
其中
node: 被克隆节点
deep: 可选,默认为 false。如果传递给它的参数是 true,它还将递归复制当前节点的所有子孙节点。否则,它只复制当前节点,注意,只会复制到标签,而不会包括里面的文本内容
。
所以可以将代码段中的 box.appendChild(targetChildrens[i])
改为如下,实现复制而不是移动追加:
box.appendChild( targetChildrens[i].cloneNode(true) );
对于动态生成(通过createElement创建)的节点,则可以直接使用 appendChild() 来追加。
<div id="box">
<p>参考的P标签元素</p>
</div>
<script>
var box = document.getElementById("box");
var target = document.getElementById("target");
for(var i = 1; i <= 5; i++){
var p = document.createElement("p");
var textNode = document.createTextNode('动态添加的p标签' + i);
p.appendChild(textNode);
box.appendChild(p);
}
</script>
当没使用 cloneNode()
时,细心的你也许会发现,代码段运行结果跟自己想象的有点不一样,如下图:
你也许会有疑问,为啥只有1、3、5被追加了,而2、4还留在原地呢?其实就跟开始讲的 appendChild 移动
操作有关。
让我们来分析一下,因为有5个节点,所以会遍历5次:
1)、第一次遍历(i 的值为0
)移动追加了节点1( "p text 1"
),这个没问题;
2)、第二次遍历,由于节点对象 targetChildrens
是一个对象,存在引用的问题,第1次遍历完后, targetChildrens
中节点1被移走后只剩下4个元素,如下:
{
0: <p>p text 2</p>,
1: <p>p text 3</p>,
2: <p>p text 4</p>,
3: <p>p text 5</p>
}
此时 i 的值为1
,所以追加了 "p text 3"
;
3)同理,第三次遍历, targetChildrens
中节点3被移走后只剩下3个元素,如下:
{
0: <p>p text 2</p>,
1: <p>p text 4</p>,
2: <p>p text 5</p>
}
此时 i 的值为2
,,所以追加了 "p text 5"
;
4)其实没有第四次遍历了,就是说我们初始想法中理所当然的五次遍历是 错误
的,因为经过第三次遍历之后,targetChildrens.length === 2
,而此时 i 的值已为3
,不再满足遍历的条件。
通过打印输出,我们可以得到验证结果:
var targetChildrens = target.children;
for (var i = 0; i < targetChildrens.length; i++) {
console.log("i", i);
console.log("targetChildrens", targetChildrens);
box.appendChild(targetChildrens[i]);
}
当使用 cloneNode()
后,由于是克隆节点来追加,所以不会存在上面的问题:
box.appendChild( targetChildrens[i].cloneNode(true) );
后记,与 appendChild()
相似的方法还有 insertBefore()
。