Edited at

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


問題

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]


参考