如何在 Javascript 中将 DOM 节点列表转换为数组?

IT技术 javascript html dom
2021-01-21 18:57:46

我有一个 Javascript 函数,它接受一个 HTML 节点列表,但它需要一个 Javascript 数组(它在上面运行一些 Array 方法),我想向它提供Document.getElementsByTagName返回 DOM 节点列表的输出

最初我想使用一些简单的东西,比如:

Array.prototype.slice.call(list,0)

这在所有浏览器中都可以正常工作,当然 Internet Explorer 会返回错误“预期的 JScript 对象”,因为显然Document.getElement*方法返回的 DOM 节点列表不是一个足以成为函数调用目标的 JScript 对象。

警告:我不介意编写 Internet Explorer 特定的代码,但我不允许使用任何 Javascript 库,例如 JQuery,因为我正在编写一个嵌入到 3rd 方网站的小部件,并且我无法加载外部库会给客户造成冲突。

我最后的努力是遍历 DOM 节点列表并自己创建一个数组,但有没有更好的方法来做到这一点?

6个回答

es6 中你可以使用如下:

  • 展开运算符

     var elements = [... nodelist]
    
  • 使用 Array.from

     var elements = Array.from(nodelist)
    

更多参考https://developer.mozilla.org/en-US/docs/Web/API/NodeList

@vsync(tl;dr - 并非所有听众都是优秀的程序员)。我认为这取决于你如何格式化你的答案:1. 你可以把你的代码示例放在一个块中,而不是内联 2. 编写“nodelist”而不是“document.querySelectorAll('p')”对某些人来说不那么可怕堆栈 3 上的“程序员”。提到 babel 可能吓跑了一些人,因为回答问题 4 并不是绝对必要的。包括“for(p of ...”的例子可能让一些人感到困惑,因为他们没有知道“of”是如何工作的。 5. 链接到 MDN 给人们一种权威感(正确或错误......)
2021-03-18 18:57:46
@vsync,你的回答没有提到 Array.from
2021-03-19 18:57:46
在你之前一年回答了同样的问题,你在选票上通过了我?我无法解释这个..
2021-03-30 18:57:46
如果有人在 Typescript(到 ES5)中使用这种方法,则只能Array.from工作,因为 TS 将其转换为nodelist.slice- 不支持。
2021-04-05 18:57:46
如此简单Array.from():D
2021-04-10 18:57:46

NodeLists 是宿主对象Array.prototype.slice不能保证在宿主对象上使用该方法,ECMAScript 规范指出:

切片功能能否成功应用于宿主对象取决于实现。

我建议您创建一个简单的函数来迭代NodeList并将每个现有元素添加到数组中:

function toArray(obj) {
  var array = [];
  // iterate backwards ensuring that length is an UInt32
  for (var i = obj.length >>> 0; i--;) { 
    array[i] = obj[i];
  }
  return array;
}

更新:

正如其他答案所暗示的那样,您现在可以在现代环境中使用传播语法或Array.from方法:

const array = [ ...nodeList ] // or Array.from(nodeList)

但是仔细想想,我想将 NodeList 转换为 Array 的最常见用例是对其进行迭代,现在NodeList.prototype对象forEach本身就有方法,所以如果你在现代环境中,你可以直接使用它,或者有一个pollyfill。

在什么情况下会obj.length是整数值以外的任何东西?
2021-03-15 18:57:46
我不敢相信事情这么复杂。丑陋的。这是 Web/JS 编程中非常普遍的需求。下一版本语言的新方法?
2021-03-20 18:57:46
这是创建一个数组,其中列表的原始顺序颠倒了,我认为这不是 OP 想要的。你的意思是做array[i] = obj[i]而不是array.push(obj[i])
2021-04-06 18:57:46
@Tim,对,我以前有过这样的经历,但昨天晚上在没有注意到的情况下进行了编辑(当地时间凌晨 3 点 :),谢谢!。
2021-04-06 18:57:46
@AlbertoPerez,不客气!Saludos hasta马德里!
2021-04-09 18:57:46

使用spread (ES2015),就像:[...document.querySelectorAll('p')]

(可选:使用Babel将上述 ES6 代码转译为 ES5 语法)


在浏览器的控制台中尝试一下,看看它的神奇之处:

for( links of [...document.links] )
  console.log(links);
或者在执行前将其转译为 es5
2021-03-25 18:57:46
@OmidHezaveh - 正如我所说,这是 ES6 代码。我不知道 Chrome 44 是否支持 ES6,如果支持,覆盖范围如何。它已经使用了将近一年的浏览器,显然您必须在支持 ES6 传播的浏览器上运行此代码。
2021-03-28 18:57:46
至少在最新的 chrome,44,我得到了这个:Uncaught TypeError: document.querySelectorAll is not a function(...)
2021-04-05 18:57:46

使用这个简单的技巧

<Your array> = [].map.call(<Your dom array>, function(el) {
    return el;
})
你能解释一下为什么你认为这比使用Array.prototype.slice(或[].slice如你所说)有更好的成功机会吗?作为说明,我想评论一下我在 Q 中记录的 IE 特定错误发生在 IE 8 或更低版本中,map无论如何都没有实现。在IE 9(“标准模式”)或更高,两者slicemap以同样的方式成功。
2021-04-11 18:57:46

虽然它不是一个真正合适的垫片,因为没有要求使用 DOM 元素的规范,我已经制作了一个允许您以slice()这种方式使用https : //gist.github.com/brettz9/6093105

更新:当我用 DOM4 规范的编辑器提出这个问题时(询问他们是否可以向宿主对象添加自己的限制(以便规范要求实现者在与数组方法一起使用时正确转换这些对象)超出 ECMAScript 规范允许实现独立),他回答说“根据 ES6 / IDL,主机对象或多或少已经过时了。” 我看到每个http://www.w3.org/TR/WebIDL/#es-array规范可以使用这个 IDL 来定义“平台数组对象”,但http://www.w3.org/TR/domcore/没有似乎没有将新的 IDL 用于HTMLCollection(尽管它看起来可能会这样做,Element.attributes尽管它只是明确声明它正在将 WebIDL 用于 DOMString 和 DOMTimeStamp)。我确实看到[ArrayClass](继承自 Array.prototype)用于NodeListNamedNodeMap现在已弃用,取而代之的是唯一仍在使用它的项目,Element.attributes)。不管怎样,看来要成为标准了。ES6Array.from也可能比必须指定更方便,Array.prototype.slice并且在语义上更清晰[].slice()Array.slice()据我所知,更短的形式(“数组泛型​​”)尚未成为标准行为)。

我已经更新以表明规范可能会朝着要求这种行为的方向发展。
2021-03-19 18:57:46