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 を投げてしまう (SafariGecko では名前空間が空ということにしてくれる)。つまり 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++) とするほうが目に見えて速かったのだが、ループ内の処理によるのかな? 単純処理なら両方同じくらいだった。