Join MDN and developers like you at Mozilla's View Source conference, 12-14 September in Berlin, Germany. Learn more at https://viewsourceconf.org

XMLHttpRequest の HTML パース処理

XMLHttpRequest 仕様は HTML のパース処理をサポートしました (これまで XMLHttpRequest は XML のパース処理しかサポートしていませんでした)。これによって、Web アプリは XMLHttpRequest から HTML をパース済の DOM として取得できます。

制限

非同期ではない XMLHttpRequest の利用を推奨しないことから、同期モードではサポートされていません。また、HTML のサポートは responseType プロパティの値が "document" にセットされているときにのみ有効です。この制限を課すことにより、デフォルトモードで responseTexttext/html なリソースを取得する XMLHttpRequest を利用する古いコードが、リソースを不必要に HTML としてパースする必要がなくなります。また、HTTP エラーページについて responseXMLnull を返すことを想定した古いコードで問題が起こることも防げます (HTTP エラーページはレスポンスボディが text/html であることが多いのです)。

HTML パース処理の利用

XMLHttpRequest で HTML を DOM として取得するのは、XML を DOM として取得するのとそう変わりません。ただ、非同期モードを利用しなければいけないことと、XMLHttpRequest オブジェクトの open() をコールしたあと、send() をコールする前に responseType プロパティに "document" を指定し、明示的に文書をリクエストしなければいけないという違いがあります。

var xhr = new XMLHttpRequest();
xhr.onload = function() {
  alert(this.responseXML.title);
}
xhr.open("GET", "file.html");
xhr.responseType = "document";
xhr.send();

HTML サポートの検出

方法 1 ― 同期モードのエラーを利用

この方法は「強制的に非同期」である性質を利用するものです。XMLHttpRequest オブジェクトの responseType を同期モードで呼び出した後にセットするのです。これは HTML パース処理をサポートするブラウザではエラーを投げることになります。サポートしないブラウザでは、エラーが起こりません。

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;
}

jsFiddle で確認

この方法は同期モードで行われるため、外部リソースに頼る必要がありません。しかし、実際の機能ではなくサポートされていそうという状況を確かめるものなので、次に紹介する方法よりも確実なものではありません。

方法 2 ― 実際に取得し確実に検出

XMLHttpRequest で HTML のパース処理がサポートされているかを確実に確かめるのには2つの課題があります。まず、この機能が非同期モードでしかサポートされていないことから、検出した結果が非同期であることを確かめなければいけません。そして、テストする文書が HTTP 上になければいけません。なぜなら、data: URL による取得では data: URL のサポートも確かめなければいけないからです。

つまり、HTML サポートを検出するには、サーバ上にテスト用の HTML 文書が必要なのです。このテストファイルは小さく、そして well-formed な XML 文書であってはいけません。

<title>&amp;&<</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 を、サポートがない場合は false を引数にとり、非同期にコールされる関数です。

jsFiddle で確認

文字エンコーディング

HTTP の Content-Type ヘッダで文字エンコーディングが宣言されている場合は、そのエンコーディングが使用されます。そうでない場合、もし byte order mark がある場合は、その BOM が示すエンコーディングを使用します。そうでない場合、もしファイルの先頭 1024 バイト以内にエンコーディングを宣言する meta 要素がある場合は、そのエンコーディングが使用されます。それもない場合、ファイルは UTF-8 としてデコードされます。

ブラウザの互換性

機能 Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
サポート 18 11 10 --- 未サポート
(535.14)
機能 Android Firefox Mobile (Gecko) IE Phone Opera Mobile Safari Mobile
サポート --- 11 --- --- ---

仕様

ドキュメントのタグと貢献者

 このページの貢献者: ethertank, DavidWalsh, Potappo, ziyunfei, myakura
 最終更新者: ethertank,