###前提・実現したいこと
次のHTMLからテキストノード値が「JavaScript」となる「XPath 式」を作りたい。
HTML
1<div id="sample"> 2 <p>JavaScript<span>test</span>JavaScript<span>JavaScript</span>test<span>test</span>JavaScript<span>test</span>JavaScript</p> 3 <p>JavaScript<span>test</span>JavaScript<span>JavaScript</span>test<span>test</span>JavaScript<span>test</span>JavaScript</p> 4</div>
###発生している問題・エラーメッセージ
document.evaluate
で実装出来ましたが、テキストノード値の照合は for
ループ内でフィルタする方法しか思いつきませんでした。
###ソースコード
XPathResult.ORDERED_NODE_ITERATOR_TYPE
を指定すれば for-of
や Array.from
でループコストが安くなりますが、Polyfill が難しいので snapShot
で妥協しています。
TreeWalker
は代替案です。
document.createTreeWalker()
時点で「テキストノード値 === "JavaScript"」を得られるのは優秀ですが、クロージャが形成されている点がもやもやします。
JavaScript
1/** 2 * XPath 式 3 */ 4function getTextNodes1 (contextNode, data) { 5 var doc = contextNode.nodeType === Node.DOCUMENT_NODE ? contextNode : contextNode.ownerDocument, 6 xpathResult = doc.evaluate('descendant::text()', contextNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null), 7 i = 0, 8 l = xpathResult.snapshotLength, 9 textNodes = [], 10 textNode; 11 12 while (i < l) { 13 textNode = xpathResult.snapshotItem(i++); 14 15 if (textNode.data === data) { 16 textNodes.push(textNode); 17 } 18 } 19 20 return textNodes; 21} 22 23/** 24 * TreeWalker 25 */ 26function getTextNodes2 (contextNode, data) { 27 function filter (textNode) { 28 return textNode.data === data ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; 29 } 30 31 var doc = contextNode.nodeType === Node.DOCUMENT_NODE ? contextNode : contextNode.ownerDocument, 32 treeWalker = doc.createTreeWalker(contextNode, NodeFilter.SHOW_TEXT, filter, false), 33 textNodes = []; 34 35 while (treeWalker.nextNode()) { 36 textNodes.push(treeWalker.currentNode); 37 } 38 39 return textNodes; 40} 41 42console.log(getTextNodes1(document.getElementById('sample'), 'JavaScript')); 43console.log(getTextNodes2(document.getElementById('sample'), 'JavaScript'));
###質問
XPath 式のみで目的のテキストノードの XpathResult
を参照する方法はないでしょうか。
###回答
@ryls-nmm さんの回答により下記 XPath 式で解決できる事がわかりました(1つめの XPath 式は短縮形)。
descendant::text()[.="JavaScript"] descendant::text()[self::node()="JavaScript"]
参考リンク

回答1件
あなたの回答
tips
プレビュー