問題
document.evaluate は第 4 引数に以下のどちらかを指定した場合、専用のイテレータを返す。
XPathResult.UNORDERED_NODE_ITERATOR_TYPE
XPathResult.ORDERED_NODE_ITERATOR_TYPE
ただし、このイテレータは iterable プロトコル を満たしていないため for...of や Array.from() が使えない
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]