This jsFiddle说明了这个问题。如果我正确理解正在发生的事情,当我迭代并修改NodeList
时,计数器变量i
会错过其他所有节点。
在那个小提琴中,我有两个列表#one
和#two
,我想将#one
的所有孩子移到#two
-
<ol id='one'>
<li class='move'>one</li>
<li class='move'>two</li>
<li class='move'>three</li>
<li class='move'>four</li>
<li class='move'>four</li>
</ol>
<ol id='two'>
</ol>
使用一些最小的JavaScript
var lisToMove = document.getElementsByClassName('move');
var destination = document.getElementById('two');
for (var i = 0; i < lisToMove.length; i++) {
destination.insertBefore(lisToMove[i], null);
}
我知道我可以通过简单地将NodeList
转换为Array
并迭代Array
来解决此问题,但我想知道迭代{{1}的正确方法是什么1}}如果要修改NodeList本身(而不仅仅是节点)是?
答案 0 :(得分:2)
我不确定是否有&#34;对&#34;办法。无论您最容易实现什么,易于理解,并且有效,应该是您使用的。 slice
的简单NodeList
不应该是一个问题,即使只是创建一个新数组并复制对其内容的引用,这是最微小的性能影响。
如果您真的不想复制数组,可以向后循环NodeList
,保证您对列表中该节点所做的任何更改实际上都不会影响列表
这是我的意思的一个例子:
var lisToMove = document.getElementsByClassName('move');
var destination = document.getElementById('two');
var i = lisToMove.length;
while (i--) {
destination.insertBefore(lisToMove[i], destination.firstChild);
}
DEMO: http://jsfiddle.net/TYZPD/
正如您所看到的,它不是循环中的直接变化 - 插入节点的位置/方式的逻辑也必须改变,因为一切都是向后的。这就是为什么我将第二个参数更改为insertBefore
为destination.firstChild
的原因。我确定还有其他处理逻辑的方法,但原理是一样的。
<强>参考文献:强>
Node.firstChild
- https://developer.mozilla.org/en-US/docs/Web/API/Node.firstChild Node.insertBefore
- https://developer.mozilla.org/en-US/docs/Web/API/Node.insertBefore 答案 1 :(得分:2)
节点列表通常实现为带有过滤器的节点迭代器。这意味着获取像length这样的属性是O(n),并通过重新检查长度迭代列表将是O(n ^ 2)。
var paragraphs = document.getElementsByTagName('p');
for (var i = 0; i < paragraphs.length; i++) {
doSomething(paragraphs[i]);
}
最好这样做:
var paragraphs = document.getElementsByTagName('p');
for (var i = 0, paragraph; paragraph = paragraphs[i]; i++) {
doSomething(paragraph);
}
这适用于所有集合和数组,只要该数组不包含被视为boolean false的内容。
如果您正在迭代childNodes,您还可以使用firstChild和nextSibling属性。
var parentNode = document.getElementById('foo');
for (var child = parentNode.firstChild; child; child = child.nextSibling) {
doSomething(child);
}
答案 2 :(得分:1)
使用数组 是执行此类操作的正确方法。就像你必须修改一个数组而在它上面循环一样,你可能想要将修改部分和循环部分分开,这样就不会影响另一个。
在这种情况下,通过将NodeList转换为数组来完成此操作。
根据Benjamin Gruenbaum的建议,您还可以使用querySelectorAll
,它会返回一个冻结的NodeList。