XMLHttpRequest における HTML の扱い
W3C の XMLHttpRequest
仕様書では、もともと XML の解析しか対応していなかった XMLHttpRequest
に HTML の解析を追加しています。この機能によって、ウェブアプリは XMLHttpRequest
を使って HTML を解析済の DOM として取得することができます。
一般的な XMLHttpRequest
の使い方についての概要は、 XMLHttpRequest の利用をお読みください。
制限
同期的な XMLHttpRequest
の利用を避けるために、 HTML 対応は同期モードでは利用できません。また、 HTML 対応は responseType
プロパティが "document"
に設定されている時にのみ有効です。この制限によって、古いコードが XMLHttpRequest
を使って responseText
が text/html
であるリソースを既定のモードで受け取るときに、無用に HTML を解釈する時間を浪費することを防ぎます。また、この制限によって HTTP のエラーページ (ふつうは text/html
の応答本文を持つ) の際に responseXML
が null
と想定する古いコードで問題が発生することを防ぐこともできます。
使用方法
XMLHttpRequest
を使って HTML リソースを DOM として取得することは、 XMLHttpRequest
を使って XML リソースを DOM として取得するのと似ていますが、同期モードを使用することはできず、 XMLHttpRequest
オブジェクトの open()
を呼び出した後、 send()
を呼び出す前に、 responseType
プロパティに文字列 "document"
代入して、明示的に文書を要求する必要があるという点が異なります。
var xhr = new XMLHttpRequest();
xhr.onload = function() {
console.log(this.responseXML.title);
}
xhr.open("GET", "file.html");
xhr.responseType = "document";
xhr.send();
機能の検出
方法 1
この方法は「強制的に非同期」である性質を利用するものです。 XMLHttpRequest
オブジェクトを同期モードで開いた後、 responseType
設定しようとすると、機能を実装しているブラウザーではエラーを投げますが、それ以外のブラウザーではそのまま動作します。
function HTMLinXHR() {
if (!window.XMLHttpRequest)
return false;
var req = new window.XMLHttpRequest();
req.open('GET', window.location.href, false);
try {
req.responseType = 'document';
} catch(e) {
return true;
}
return false;
}
この方法は同期的であり、他の資産に頼りませんが、この機能があることを示すだけで実際の機能をチェックするものではないので、次の方法 2 の方がより信頼できるかもしれません。
方法 2
ブラウザーが XMLHttpRequest
で HTML の解析処理に対応しているかどうかを確実に検出するには、二つの課題があります。まず、 HTML 対応が非同期モードでしか有効でないことから、検出結果は非同期で受け取られることになります。第二に、 data:
URL を使用すると同時に data:
URL の対応にも依存することになるため、実際に HTTP を通じて文書を取得しなければならないことです。
つまり、 HTML 対応を検出するには、サーバ上にテスト用の HTML 文書が必要になります。このテストファイルは小さく、整形式の XML ではないものです。
<title>&&<</title>
このファイルが detect.html
という名前だった場合、 HTML 対応を検出する関数は次のように書くことができます。
function detectHtmlInXhr(callback) {
if (!window.XMLHttpRequest) {
window.setTimeout(function() { callback(false); }, 0);
return;
}
var done = false;
var xhr = new window.XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && !done) {
done = true;
callback(!!(this.responseXML && this.responseXML.title && this.responseXML.title == "&&<"));
}
}
xhr.onabort = xhr.onerror = function() {
if (!done) {
done = true;
callback(false);
}
}
try {
xhr.open("GET", "detect.html");
xhr.responseType = "document";
xhr.send();
} catch (e) {
window.setTimeout(function() {
if (!done) {
done = true;
callback(false);
}
}, 0);
}
}
引数の callback
は非同期に呼び出される関数であり、 HTML 対応がある場合には唯一の引数が true
になり、 HTML 対応がない場合は唯一の引数が false
になります。
文字エンコーディング
HTTP の Content-Type
ヘッダーで文字エンコーディングが宣言されている場合は、そのエンコーディングが使用されます。そうでない場合、もしバイトオーダーマークがある場合は、そのバイトオーダーマークが示すエンコーディングを使用します。そうでない場合、もしファイルの先頭 1024 バイト以内にエンコーディングを宣言する <meta>
要素がある場合は、そのエンコーディングが使用されます。それもない場合、ファイルは UTF-8 としてデコードされます。
古いブラウザーでの HTML の扱い
XMLHttpRequest
はもともと、 XML の解析のみ対応していました。 HTML の解析は最近追加されたものです。古いブラウザーでも、 XMLHttpRequest.responseText
プロパティと正規表現の組み合わせで、例えば、指定された ID の HTML 要素のソースコードを取得することができます。
function getHTML (oXHR, sTargetId) {
var rOpen = new RegExp("<(?!\!)\\s*([^\\s>]+)[^>]*\\s+id\\=[\"\']" + sTargetId + "[\"\'][^>]*>" ,"i"),
sSrc = oXHR.responseText, aExec = rOpen.exec(sSrc);
return aExec ? (new RegExp("(?:(?:.(?!<\\s*" + aExec[1] + "[^>]*[>]))*.?<\\s*" + aExec[1] + "[^>]*[>](?:.(?!<\\s*\/\\s*" + aExec[1] + "\\s*>))*.?<\\s*\/\\s*" + aExec[1] + "\\s*>)*(?:.(?!<\\s*\/\\s*" + aExec[1] + "\\s*>))*.?", "i")).exec(sSrc.slice(sSrc.indexOf(aExec[0]) + aExec[0].length)) || "" : "";
}
var oReq = new XMLHttpRequest();
oReq.open("GET", "yourPage.html", true);
oReq.onload = function () { console.log(getHTML(this, "intro")); };
oReq.send(null);
メモ: この方法はインタープリターにとってとても重いものです。本当に必要なときのみ使用してください。
仕様書
Specification |
---|
XMLHttpRequest Standard # interface-xmlhttprequest |
ブラウザーの対応
XMLHttpRequest インターフェイス
BCD tables only load in the browser