document.evaluateと名前空間がわからない
m.twitter.com/home で document.evaluate しようとしても、結果が空集合。
javascript:alert(document.evaluate('//a',document,null,7,null).snapshotLength);void(0);
(第4引数の7というやつは "XPathResult.ORDERED_NODE_SNAPSHOT_TYPE" の代用)
こっちはOK。
javascript:var myresolver=function(){return 'http://www.w3.org/1999/xhtml';}; alert(document.evaluate('//xhtml:a',document,myresolver,7,null).snapshotLength);void(0);
しかし、上のやつがちゃんと動くサイトでは下のやつは動かない。
HTMLヘッダーを比べてみても
//m.twitter <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> //別のあるサイト <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
と、違いがあるようには見えないのだけど。
ここらへんをきちんと吸収してくれるのがこの $X 関数だったと思ったのだけど、どうもうまくいかない。
使い方間違ってるのかな?
誰かアドバイス下さい。
os0x さんから丁寧にお返事を頂いた。
つまり本家の $X が動いていなかったそうなのだが、書き直してもらったこれ (↓みたいに名前空間 resolver を書くやつ)
function (prefix) {return document.createNSResolver(context).lookupNamespaceURI(prefix) || context.namespaceURI || document.documentElement.namespaceURI || "";}
も自分のところではも NAMESPACE_ERR となる。↓と同じケースだろう。
Opera (9.24/9.5) では resolver が "" (空文字列) をかえすと、null を返したときと同じように NAMESPACE_ERR を投げてしまう (Safari や Gecko では名前空間が空ということにしてくれる)。つまり Opera だと全適用のユーザスクリプトで XPath つかうときはページが XML か HTML かで式自体を変えないといけないのでめんどい。
XPathNSResolver のクロスブラウザとか - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech
そういうわけで自分の用途では、一度 html で XPath 結果を取ってみて、空集合なら上の resolver を使うようにした。
そのコードの断片。
var exp = '//'+['a','input','textarea','select'].join('|//'); var result = document.evaluate(exp,document,null,7,null); var len = result.snapshotLength; if(!len){ exp = exp.replace('//','//x:'); resolv = function (prefix) {return document.createNSResolver(document).lookupNamespaceURI(prefix) || document.namespaceURI || document.documentElement.namespaceURI;}; result = document.evaluate(exp,document,resolv,7,null); len = result.snapshotLength; if(!len) return; }
あと、何故か知らないが、Iterator Type で取ってきて while(e=result.iterateNext())
で回すより Snapshot Type で for(var i=0;i< len;i++)
とするほうが目に見えて速かったのだが、ループ内の処理によるのかな? 単純処理なら両方同じくらいだった。