如何将Array.from与XPathResult一起使用?

时间:2017-10-30 14:14:16

标签: javascript arrays xpath

当我使用querySelectorAll时,我可以在示例文档中找到138个td个节点。

Array.from(document.querySelectorAll('td')).length
138

当我对XPath做同样的事情时,我得不到任何结果:

Array.from(document.evaluate(".//td", document.body, null, XPathResult.ANY_TYPE, null)).length
0

虽然至少有一场比赛:

document.evaluate(".//td", document.body, null, XPathResult.ANY_TYPE, null).iterateNext().nodeName
"TD"

问题似乎是Array.from无法迭代XPathResult。即便这样也会返回0:

Array.from(document.evaluate('.', document.body, null, XPathResult.ANY_TYPE, null)).length
0

如何使XPathResult适合Array.from

2 个答案:

答案 0 :(得分:4)

不幸的是你做不到。 Array.from可以将两种类型的对象转换为数组:

  1. 那些具有.length属性的“数组类似”。
  2. 实现迭代器协议并允许您获取所有元素的那些。
  3. XPathResult没有做任何这些。您可以通过手动迭代结果并将结果存储在数组中来实现:

    const nodes = [];
    let node = xPathResult.iterateNext();
    while (node) {
      nodes.push(node);
      node = xPathResult.iterateNext();
    }
    

    ...但是如果你要在节点上循环,你可以在循环中做你想要做的任何数组操作。

答案 1 :(得分:0)

正如现有答案所述,这本身并不“受支持”,因为(出于某种奇怪的原因)XPathResult 不支持 iterable protocol,即使您使用 ORDERED_NODE_ITERATOR_TYPE .

如果您真的想将有序节点迭代器类型 XPath 结果用作 JavaScript 迭代器,那么这当然是可行的:

xpr => ({[Symbol.iterator]: () => ({next: () => {
  let node = xpr.iterateNext();
  return {value: node, done: !node}
}})})

这自然适用于您的用例Array.from

// As an example, pull the top child elements -- should just be the <head> and <body>
let example = document.evaluate('/html/*', document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);

Array.from((xpr => ({[Symbol.iterator]: () => ({next: () => {
  let node = xpr.iterateNext();
  return {value: node, done: !node}
}})}))(example))

...不过,它支持for...of

let example2 = document.evaluate('/html/*', document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);

for( let e  of  (xpr => ({[Symbol.iterator]: () => ({next: () => {
  let node = xpr.iterateNext();
  return {value: node, done: !node}
}})}))(example2) ) {
  // It's iterable!
  console.log(e.tagName);
}

因此,这个解决方案稍微更通用(并且在不实例化数组的情况下迭代大量节点时,它也应该使用更少的内存)。