それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

document.evaluate でXPATH する具体的サンプル

javascript でノードを拾うのCSSセレクタ(document.querySelector/querySelectorAll)だけなく、Xpathも使いたいよねってこと。DOM Level3も使えるし。

jQueryは使われすぎてバージョンHellで・・・
DOM の Evaluete/Xpath は今後はどうなるかわからないけど使えなくなるってのはなさそうだし

具体的サンプルの前に。

ブラウザでXpathを手軽に実行できる、document.evaluate 関数ですが、理屈が分からないと使いにくいので、document.xpathを紹介します。

document.evaluate でXPATH

実際にノードを探して使うサンプル

document.evaluate('//input[@value="削除"]',document,null,XPathResult.ANY_TYPE,null)

結果は、XPathResultオブジェクトになって取得される。

結果は、必ずしも配列にならない。(countなど)

結果がiterable のときはiterateNext()を使って順番に取り出せるので配列に変換できる。

ただし、XPathResultは node.remove()後に、iterateNext()がエラーになる。mutableじゃないから、dom変更後のsnapshotが使えないよってオコ(ノードへのショートカット)

結果は配列に放り込める。

複数ノードをとるのであれば、

いったん配列に入れてしまうことが便利

a=[];
while(e=ret.iterateNext()){a.push(e);};
a.forEach(function(e){console.log(e)} )

Array.from/Array.apply が使えれば良かったのに。

単一値を返すものは

ret.numberValue

プロパティーにアクセスでとれる。

なので、このよくある処理をまとめて・・・・

document.xpath 関数を作る

document.xpath = function(expression) {
  ret = document.evaluate(expression,document,null,XPathResult.ANY_TYPE,null);
  switch(ret.resultType){
   case 1: return ret.numberValue;
   case 2: return ret.stringValue;
   case 3: return ret.booleanValue;
   case 4: 
   case 5:
           var a=[];
           while(e=ret.iterateNext()){a.push(e);};
           return a;
   default: return ret;
   }
}

ここまでがよく使うワンセット。

ここからはこのxpath関数を使ってサンプルを紹介します。

いかXPATHのサンプル覚え書き

特定タグ(h1)で一覧
document.xpath("//h1")

特定タグで特定属性があるもの

document.xpath("//a[@href]")
document.xpath("//a[@onclick]")

全部のタグで特定属性があるもの

document.xpath("//*[@href]")
document.xpath("//*[@onclick]")

文字列が一致するもの

属性値が特定のもの

document.xpath("//*[@rel='stylesheet']")
document.xpath("//*[@type='text/css']")

テキストが一致するもの

document.xpath("//a[text()='次へ']")

ただし、一致は改行・タブ・空白文字が面倒なので、=の完全一致ではなく、次のように「部分一致」を行う。

文字列を「含む」もの(部分一致)

document.xpath("//link[contains(@rel, 'icon')]")
document.xpath("//a[contains(./text(),'記事')]")

ノード以外に属性やテキストが結果に欲しい。

マッチしたノードの属性値を取り出す。

document.xpath("//link[contains(@rel, 'icon')]/@rel")
document.xpath("//link[contains(@rel, 'icon')]/@href")

マッチしたノードのテキストを取り出す。

document.xpath("//a[@href]/text()")

除外(not)する条件で書く

条件を以外と書いて楽をしたい --> not 関数

document.xpath("//a[not(@href)]")

関数だけど、not なので便利

何件あるか知りたい

xpath でcount()関数を使う

document.xpath("count(//a)")

複雑な条件の組み合わせ and or

or で複数条件のいずれかにマッチするノードを取り出せる

document.xpath("//link[@type='text/css' or @rel='stylesheet']")

and で複数条件絞り込みを書くことができるが、次の2つが同じ意味なのであまり使わない

document.xpath("//*[@rel and @href and @type]")
document.xpath("//*[@rel][@href][@type]")

条件には、自ノード以外も使えるが、、使わないよね。

document.xpath("//head/*[@rel]")
document.xpath("//*[@rel][name(..)='head']")

name( expression ) で 指定されたノードの要素名になり、二つは同じことを示す。

あとは親ノード子ノード、子孫ノード

document.xpath("//a[text()='エネループ']/../..//a")

このへんは、普通のディレクトリパスと同じに考えられる

さらに応用

子ノードにformを持つTableを探す

document.xpath("//table[.//form[@name='WebMeisaiCommonInputForm']]")

子ノードにform name=myformA を持つTableを探す

document.xpath("//table[.//form[@name='myformA']]")

ある子ノードをもつ親ノードを検索できるって便利ですね。

XPATHの記述方法

Xpathにはいくつも記述方法があって覚えにくいし自信をなくす
要素の選択だけなら、まだ覚えることはできるが、
要素のテキストを数値評価して計算してその結果をマッチ条件に使うとかでき、デバッグ時に訳解らなくなる。。。

もし特定のノードからたどりたいときは

function xpath = function(expression,elem) {
  ret = document.evaluate(expression,elem,null,XPathResult.ANY_TYPE,null);
  switch(ret.resultType){
   case 1: return ret.numberValue;
   case 2: return ret.stringValue;
   case 3: return ret.booleanValue;
   case 4: 
   case 5:
           var a=[];
           while(e=ret.iterateNext()){a.push(e);};
           return a;
   default: return ret;
   }
}

追記(2019-12-10)

変数名スコープがglobal に漏れてたので修正