LoginSignup
2
0

More than 3 years have passed since last update.

[JavaScript] イテレータ型の XPathResult オブジェクトを iterable にする

Last updated at Posted at 2019-09-12

問題

document.evaluate は第 4 引数に以下のどちらかを指定した場合、専用のイテレータを返す。

  • XPathResult.UNORDERED_NODE_ITERATOR_TYPE
  • XPathResult.ORDERED_NODE_ITERATOR_TYPE

ただし、このイテレータは iterable プロトコル を満たしていないため for...ofArray.from() が使えない :sob:

const getHeaders = () => {
  const resultType = XPathResult.ORDERED_NODE_ITERATOR_TYPE;
  return document.evaluate('//h1', document, null, resultType, null);
};

const xPathResult1 = getHeaders();
for (const node of xPathResult1) console.log(node);
// Uncaught TypeError: xPathResult is not iterable

const xPathResult2 = getHeaders();
Array.from(xPathResult2);
// []

const xPathResult3 = getHeaders();
[...xPathResult3]
// Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

解決策

Generator オブジェクトに変換するとよい。なぜなら Generator オブジェクトは iterable プロトコルに従っているからだ。

const getHeaders = () => {
  const resultType = XPathResult.ORDERED_NODE_ITERATOR_TYPE;
  return document.evaluate('//h1', document, null, resultType, null);
};

// イテレータ型の XPathResult オブジェクトを Generator オブジェクトに変換する。
const makeIterable = function*(xPathResult) {
  while (true) {
    const node = xPathResult.iterateNext();
    if (!node) break;
    yield node;
  }
};

const xPathResult1 = getHeaders();
for (const node of makeIterable(xPathResult1)) console.log(node);
// h1
// h1
// h1

const xPathResult2 = getHeaders();
Array.from(makeIterable(xPathResult2));
// [h1, h1, h1]

const xPathResult3 = getHeaders();
[...makeIterable(xPathResult3)];
// [h1, h1, h1]

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0