【问题标题】:How to convert a DOM node list to an array in Javascript?如何将 DOM 节点列表转换为 Javascript 中的数组?
【发布时间】:2010-04-29 05:59:39
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 更好的是,创建一个从 DOM 节点列表转换的函数,但这确实是我的解决方案,我认为你是对的。
  • > for (i=0;i
  • 这是一个非常老的问题,但是 jQuery 是专门使用 .noConflict 方法构建的,因此它不会与其他库(甚至它本身)造成冲突,这意味着多个版本jQuery 可以加载到一个页面上。也就是说,除非万不得已,否则最好避免使用/加载库。
  • @vol7ron:快进到 2016 年,每个人仍然对 JavaScript 库添加到页面的大小感到紧张。诚然,JQuery 缩小和 gzip 压缩后为 30KB,仅转换节点列表仍为 30KB 太多了 :-)

标签: javascript html dom


【解决方案1】:

es6 中你可以按如下方式使用:

  • 扩展运算符

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

     var elements = Array.from(nodelist)
    

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

【讨论】:

  • Array.from() 很容易:D
  • 如果有人在 Typescript(到 ES5)中使用这种方法,只有 Array.from 有效,因为 TS 将其转换为 nodelist.slice - 这是不支持的。
  • 在你和你投票通过我之前我回答了同样的问题?我无法解释这个..
  • @vsync,你的回答没有提到Array.from
  • @vsync(tl;dr - 并非所有观众都是优秀的程序员)。我认为这取决于您如何格式化您的答案: 1. 您可以将代码示例放在一个块中,而不是内联 2. 编写“nodelist”而不是“document.querySelectorAll('p')”对某些人来说不那么可怕堆栈 3 上的“程序员”。提到 babel 可能吓跑了一些人,因为没有必要回答问题 4。包括“for( p of ...”) 示例可能会使一些人感到困惑,因为他们没有知道“of”是如何工作的。5.链接到 MDN 给人们一种权威感(无论对错……)
【解决方案2】:

NodeLists 是 host objects,不能保证在宿主对象上使用 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 method natively,所以如果你在现代环境中,你可以直接使用,或者有一个 pollyfill。

【讨论】:

  • 这是创建一个与列表的原始顺序相反的数组,我认为这不是 OP 想要的。你的意思是用array[i] = obj[i] 而不是array.push(obj[i])
  • @Tim,对,我以前也有过,但昨天晚上在没有注意到的情况下编辑了它(当地时间凌晨 3 点 :),谢谢!。
  • 在什么情况下obj.length 不是整数值?
  • 我不敢相信它这么复杂。丑陋。这是 Web/JS 编程中非常普遍的需求。下一个语言版本的新方法?
  • @AlbertoPerez,不客气! Saludos hasta Madrid!
【解决方案3】:

使用spread (ES2015),就这么简单:[...document.querySelectorAll('p')]

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


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

for( links of [...document.links] )
  console.log(links);

【讨论】:

  • 至少在最新的 chrome,44,我明白了:Uncaught TypeError: document.querySelectorAll is not a function(...)
  • @OmidHezaveh - 正如我所说,这是 ES6 代码。我不知道 Chrome 44 是否支持 ES6,如果支持,覆盖范围如何。它已经有将近一年的历史了,显然您必须在支持 ES6 传播的浏览器上运行此代码。
  • 或者执行前转译成es5
【解决方案4】:

使用这个简单的技巧

<Your array> = [].map.call(<Your dom array>, function(el) {
    return el;
})

【讨论】:

  • 你能解释一下为什么你认为这比使用Array.prototype.slice(或者你所说的[].slice)更有可能成功吗?作为说明,我想评论一下我在 Q 中记录的 IE 特定错误发生在 IE 8 或更低版本中,其中 map 无论如何都没有实现。在 IE 9(“标准模式”)或更高版本中,slicemap 以相同的方式成功。
【解决方案5】:

虽然它并不是一个真正合适的 shim,但由于没有规范要求使用 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/ 似乎没有为 HTMLCollection 使用新的 IDL(尽管看起来它可能正在为 @987654326 使用新的 IDL @ 虽然它只明确声明它正在使用 WebIDL 来处理 DOMString 和 DOMTimeStamp)。我确实看到[ArrayClass](继承自Array.prototype)用于NodeList(现在不推荐使用NamedNodeMap,取而代之的是唯一仍在使用它的项目Element.attributes)。无论如何,它看起来像是要成为标准。对于这种转换,ES6 Array.from 也可能比必须指定 Array.prototype.slice 更方便,并且比 [].slice() 更清晰的语义(以及更短的形式,Array.slice()(“数组泛型​​”),就我知道,不会成为标准行为)。

【讨论】:

  • 我已更新以表明规范可能正朝着要求此行为的方向发展。
【解决方案6】:

今天,在 2018 年,我们可以使用 ECMAScript 2015(第 6 版)或 ES6,但不是所有的浏览器都能理解它(例如,IE 不能理解所有它)。如果你愿意,你可以使用 ES6,如下所示: var array = [... NodeList]; (as spread operator) 或 var array = Array.from(NodeList);

在其他情况下(如果您不能使用 ES6),您可以使用最短的方法将 NodeList 转换为 Array

var array = [].slice.call(NodeList, 0);.

例如:

var nodeList = document.querySelectorAll('input');
//we use "{}.toString.call(Object).slice(8, -1)" to find the class name of object
console.log({}.toString.call(nodeList).slice(8, -1)); //NodeList

var array = [].slice.call(nodeList, 0);
console.log({}.toString.call(array).slice(8, -1)); //Array

var result = array.filter(function(item){return item.value.length > 5});

for(var i in result)
  console.log(result[i].value); //credit, confidence
<input type="text" value="trust"><br><br>
<input type="text" value="credit"><br><br>
<input type="text" value="confidence">

但如果您只想简单地迭代 DOM 节点列表,则无需将 NodeList 转换为 Array。可以使用以下方法循环 NodeList 中的项目:

var nodeList = document.querySelectorAll('input');
// Calling nodeList.item(i) isn't necessary in JavaScript
for(var i = 0; i < nodeList.length; i++)
    console.log(nodeList[i].value); //trust, credit, confidence
<input type="text" value="trust"><br><br>
<input type="text" value="credit"><br><br>
<input type="text" value="confidence">

不要试图使用 for...infor each...in 来枚举列表中的项目,因为这也会枚举列表的长度和项目属性NodeList 如果您的脚本假定它只需要处理元素对象,则会导致错误。此外,for..in 不保证以任何特定顺序访问这些属性。 for...of 循环将正确循环 NodeList 对象。

参见:

【讨论】:

    【解决方案7】:
    var arr = new Array();
    var x= ... get your nodes;
    
    for (i=0;i<x.length;i++)
    {
      if (x.item(i).nodeType==1)
      {
        arr.push(x.item(i));
      }
    }
    

    这应该可以,跨浏览器并为您获取所有“元素”节点。

    【讨论】:

    • 这与@CMS 的回答基本相同,只是它假设我只需要元素节点——我不需要。
    猜你喜欢
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 2014-10-11
    • 1970-01-01
    • 2022-12-21
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多