Chromeで動作確認できたら基本的にOKにしてしまうのですが、試しにEdgeでやったら動かなくなっているヤツを発見しました(^_^;) responseType=’document’でやるとEdgeでは何故か期待通りにならない・・・なんでやねん! htmlなtextをjQueryに渡すのが素直で無難に動くのだが、cross-domainになるため相対パスな内部のurlが404エラーになることがある(そもそもなぜそのurlを参照しに行くのかが不明なんだが・・・)。結局、htmlなtextをDOMParserでdocumentに変換させるやり方にした。こちらに書いてあるようにセキュリティ的にも良さげな感じだし。これなら404参照が無くなってコンソール出力も無くなるっぽい。やれやれだぜ。
古いブラウザを考慮する必要がなければ
responseType='document'
で、古いブラウザを考慮する必要があるなら DOMParserのpolyfillを導入して、htmlなtextをdocumentに変換するのが良さげなのかも。なんかそんな気がしてきました・・・(^_^;)。
どっかのサイトのページに書かれている情報を抽出したいとします。
JavaScriptでやろうとすると、
XHR(XMLHttpRequest)でページを読み込んで、
jQueryを使って解析するという手が思いつきます。
ただCross-Domainな問題があるので、
PHPで作ったproxy経由とかで回避する必要があります。
jQueryを使ってHTMLなdocumentから情報を抽出するには、
jQuery( selector, context )
とかやります。
通常は、現在のdocumentを対象にすることが多いので、
contextの指定を省略することが多いと思います。
読み込んだページを対象にしたい時はcontextに指定すれば良いわけです。
contextとして指定できるのは、
documentオブジェクトだけでなくtextも可能です。
textで渡した場合は、jQueryの内部でdocumentオブジェクトに変換されます。
responseTypeに何も指定することなくXHRで読み込むと、
ページの中身がtextで返ってくるので、そのまま渡せばOKです。
さて、
ここから掲題の件に入ってきます。
とあるアプリを改修すべく、Chromeのデバッガを眺めていたら
なんか404なエラーが起きていることに気が付きました。
放っておいても良さげな感じだったのですが、
ちょっと気になったので調べてみました。
現象としては次のような感じ。
対象サイトのドメインをA、自サイトのドメインをBとします。
読み込んだページのHTMLをjQueryで解析しようとすると、
その中に相対パスな画像があった場合に、
本来なら「A/パス」と成るところが「B/パス」と展開されて、
そんな画像は無いので404エラーが起こっているようです。
jQueryで解析しようとするだけで、
何故そのパスを参照しに行くのか? 謎である。
しかも画像だけっぽい?
結局、原因は不明ですが、
textのままだとどのサイトから読み込んだかはわからないわけで、
相対パスな問題が起こってしまうのかもなので、
ドメインな情報も含んでいるはずの
documentとして読めばいいんじゃね?
ということで、
responseType='document'
なXHRで読み込んだら
404なエラーは出なくなりました。
めでたしめでたし。
jQueryの内部変換とかも無くなるはずだから、
こっちの方がオーバーヘッドは少ないことになるかもしれませんね。
これで解決という感じだったのですが、
古いブラウザでうまく動かないことに気が付きました。
IE8とかはもうどうでも良いので切ってしまって構わないのですが、
LTS(長期サポート)なLinuxデストリビューションだと、
prestoなOperaとかが標準ブラウザになってたりすることがあります。
具体的にはLinuxBeanとかがそれに該当します。
自分も使っているので、どうにかしたいなと(^_^;)
prestoなOperaでは、
XHRがresponseType='document'
に対応してないっぽいようです。
なのでtextとして読めばよいのですが、
そうするとChromeで404エラーが起きることになります。
要するに、
ブラウザがresponseType='document'
に
対応しているか否かをあらかじめ判定できれば、
すっきり解決できそうです。
そんなわけでWebを漁ってみたのですが、
良さげな解決法が見つかりませんでした。
ところで、
XHRのレスポンスにはresponseXML
というのがあります。
XMLでparseできた場合にここに値が入るようです。
responseType='document'
でXHRすると、
responseだけでなくresponseXMLにも同じ値が入るようです。
古い版のXHRとの互換性を図る配慮のようです。
XHRではresponseTypeを指定しなくても、
レスポンスのヘッダによって自動で変換される仕組みがあるようです。
XMLはざっくりHTMLの上位互換なようなモノのはずなので(^_^;)
overrideMimeType('text/xml')
とかやれば、
responseXMLにdocumentオブジェクトとしてゲットできるかもしれません。
実際には試してませんが、少々強引な感じがするし、
このページにあるようにresponseXMLは扱いが難しそうな感じです。
さてさて、
responseType='document'
なXHRでは、
おそらく内部でHTMLなtextからdocumentへ変換しているはずです。
その際にレスポンスで得たヘッダの情報とかを加味して
変換しているのだと思います。
ということはDOMParserが使えるかどうかで判定できるのではないか・・・。
うまい具合にこのページに判定するコードが載っていたので、
さっそくやってみました。
結論から言うと、うまく行きました。
ただ今回は、
prestoなOperaと現状で最新なChromeについてのみしか
検証してないのであしからず。
XHRにこだわってやってたりしますが、
jQuery.ajax({dataType:'xml'})
とかでやれば、
実はあっさり行けたりするのかもしれません(^_^;)
最後に、
対策コードの概要を以下に書いておきます。
var DOMParserSupported = (function() {
// Firefox/Opera/IE throw errors on unsupported types
try {
// WebKit returns null on unsupported types
if ((new DOMParser).parseFromString('', 'text/html')) {
// text/html parsing is natively supported
return true;
}
} catch (e) {}
return false;
}());
var xhr = new XMLHttpRequest();
xhr.onloadend = function() {
if (this.status === 200) {
var value = jQuery( selector, this.response);
.
.
} else {
handleError(this);
}
};
xhr.open('GET', proxy + src, true);
if (DOMParserSupported) {
xhr.responseType = 'document';
}
xhr.send();