在不转换为数组的情况下迭代 NodeList 并移动其元素的惯用方法是啥?

Posted

技术标签:

【中文标题】在不转换为数组的情况下迭代 NodeList 并移动其元素的惯用方法是啥?【英文标题】:What is the idiomatic way to iterate over a NodeList and move its elements without converting to an Array?在不转换为数组的情况下迭代 NodeList 并移动其元素的惯用方法是什么? 【发布时间】:2014-04-15 09:07:09 【问题描述】:

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 来解决此问题,但我想知道如果您正在修改 NodeList 迭代的正确方法是什么NodeList 本身(而不仅仅是节点)是?

【问题讨论】:

【参考方案1】:

使用数组做这样的事情的正确方法。就像如果你不得不修改一个数组你循环它,你可能想分开修改部分和循环部分,这样一个就不会影响另一个。

在这种情况下,通过将 NodeList 转换为数组来完成。

按照Benjamin Gruenbaum 的建议,您也可以使用querySelectorAll,它返回一个冻结的NodeList。

Demo here

【讨论】:

【参考方案2】:

我不确定是否有“正确”的方式。对你来说最容易实现、易于理解和有效的,应该是你使用的。 NodeList 中的一个简单的 slice 应该不成问题,即使简单地创建一个新数组并复制对其内容的引用会产生最小的性能影响。

如果您真的不想复制数组,可以向后循环 NodeList,确保您对列表中的该节点所做的任何更改都不会真正影响列表。

这是我的意思的一个例子:

var lisToMove = document.getElementsByClassName('move');
var destination = document.getElementById('two');
var i = lisToMove.length;
while (i--) 
    destination.insertBefore(lisToMove[i], destination.firstChild);

演示: http://jsfiddle.net/TYZPD/

正如您所看到的,这不是循环中的直接更改 - 插入节点的位置/方式的逻辑也必须更改,因为一切都是倒退的。这就是为什么我将第二个参数更改为insertBeforedestination.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

【讨论】:

【参考方案3】:

节点列表通常被实现为带有过滤器的节点迭代器。这意味着获取像长度这样的属性是 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);
 

这适用于所有集合和数组,只要数组不包含被视为布尔值 false 的内容。

如果您在子节点上进行迭代,您还可以使用 firstChild 和 nextSibling 属性。

var parentNode = document.getElementById('foo');
for (var child = parentNode.firstChild; child; child = child.nextSibling) 
  doSomething(child);

【讨论】:

以上是关于在不转换为数组的情况下迭代 NodeList 并移动其元素的惯用方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不清空 HTMLCollection 的情况下将其转换为数组?

如何在不转换为json的情况下将C#数组用于javascript数组?

如何在不迭代的情况下访问数组中的特定元素?

如何在不转换为火花数据集的情况下遍历数据框?

如何在不使用 C# 中的 T 对象的情况下将 Json 数组转换为单个 JSON 对象?

将nodeList转换为数组(兼容性)