XMLHttpRequest()

目次

概要

JavaScriptの非同期通信に利用される XMLHttpRequest() の解説まとめ。Ajax と呼ばれることもあります。

XMLHttpRequest()

// --- XMLHttpRequest

if (!'XMLHttpRequest' in this) {
  this.XMLHttpRequest = (function () {
    var i, l;

    for (i = 0, l = arguments.length; i < l; i++) {
      try {
        return arguments[i];
      }
      catch (err) {}
    }
    return null;
  })(
  function () { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); },
  function () { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); });
}

XMLHttpRequest() はモダンブラウザ(Firefox, Google Chrome, Opera等) と IE7+ から対応したメソッドですが、上記コードを取り込むことで IE6- でも標準の XMLHttpRequest() と同じように互換オブジェクトを生成できるようになります。

var req = new XMLHttpRequest();  // IE6- でもこの記述でXMLHttpRequest互換のオブジェクトを生成できる

ここでは例外処理として、null を返していますが、new XMLHttpRequest() の戻り値が null になるわけではない事に注意してください。
new演算子を使用した時点で必ずオブジェクトを生成するので、下記コードは期待通りに動作しません。

var req = new XMLHttpRequest();
if (req) {  // req値 が null か否か
  req.open ('GET', 'test.txt', true);
} else {
  throw new Error ('[object XMLHttpRequest] を生成できませんでした');
}

例外処理を行うには [object XMLHttpRequest] に存在するプロパティをみて、判断する必要があります。

var req = new XMLHttpRequest();
if ('open' in req) {  // req.open の存在チェック
  req.open ('GET', 'test.txt', true);
} else {
  throw new Error ('[object XMLHttpRequest] を生成できませんでした');
}

ActiveXObject(ProgID : String [, location : String])

XMLHttpRequest互換のオブジェクトを生成する時、new ActiveXObject() の 第一引数(ProgID) に指定される値は何種類かあります。その内のいくつかは指定すべきではないとMicrosoft中の人が指摘しています。

// --- 使うべきオブジェクト
new ActiveXObject( 'Msxml2.XMLHTTP.3.0' ); // バージョン3.0 広範に利用されているので、今後も bugfix を行う
new ActiveXObject( 'Msxml2.XMLHTTP.6.0' ); // バージョン6.0 は最新版なので bugfix を続ける

// --- 使うべきではないオブジェクト
new ActiveXObject( 'Microsoft.XMLHTTP' );  // Microsoft接頭辞は古いので指定すべきではない
new ActiveXObject( 'Msxml.XMLHTTP' );      // Msxml2接頭辞を指定すべき
new ActiveXObject( 'Msxml2.XMLHTTP' );     // バージョンを省略すると 3.0 として扱われるので、バージョンは明記すべき
new ActiveXObject( 'Msxml2.XMLHTTP.4.0' ); // バージョン4.0 は bugfix が行われないので、3.0 か 6.0 を指定すべき
new ActiveXObject( 'Msxml2.XMLHTTP.5.0' ); // バージョン5.0 は bugfix が行われないので、3.0 か 6.0 を指定すべき

キャッシュ制御

W3Cで公開されている仕様書によれば、setRequestHeader() でキャッシュ制御のためのヘッダを送信すれば、キャッシュを読み込まないように出来る、とあります。

If the user agent implements a HTTP cache it should respect Cache-Control request headers set by setRequestHeader() (e.g., Cache-Control: no-cache bypasses the cache). It must not send Cache-Control or Pragma request headers automatically unless the end user explicitly requests such behavior (e.g. by (force-)reloading the page).

For 304 Not Modified responses that are a result of a user agent generated conditional request the user agent must act as if the server gave a 200 OK response with the appropriate content. The user agent must allow setRequestHeader() to override automatic cache validation by setting request headers (e.g., If-None-Match, If-Modified-Since), in which case 304 Not Modified responses must be passed through. [RFC2616]

4.6.3 The send() method - XMLHttpRequest

モダンブラウザは no-cache でキャッシュ読み込みを回避できますが、IEは回避できないので If-Modified-Since も一緒に指定します。

var req = new XMLHttpRequest();
req.open ('GET', 'test.txt', true);
req.setRequestHeader('Pragma', 'no-cache');                                 // HTTP/1.0 における汎用のヘッダフィールド
req.setRequestHeader('Cache-Control', 'no-cache');                          // HTTP/1.1 におけるキャッシュ制御のヘッダフィールド
req.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT'); // 指定日時以降に更新があれば内容を返し、更新がなければ304ステータスを返すヘッダフィールド。古い日時を指定すれば、必ず内容を返す。

クロスドメイン制限

XMLHttpRequest() はセキュリティ上の理由から、異なるドメインにリクエストを発行できません。

XMLHttpRequest Level 2ではサーバ側で適切なヘッダを返せば、クロスドメイン制限を回避できます。

XMLHttpRequest Level 2

XMLHttpRequest Level 2 で異なるドメインにリクエストを発行するには、リクエスト先のサーバに Access-Control-Allow-Origin ヘッダフィールドを設定します。
下例では、http://example.jp からのリクエストを許可しています。

Access-Control-Allow-Origin: http://example.jp

リクエストの仕方は XMLHttpRequest() と同じですが、IE8だけは XDomainRequest のオブジェクトが別に定義されています。

var req = this.XDomainRequest ? new XDomainRequest() : new XMLHttpRequest();

サンプルコード

下記ボタンをクリックすると、XMLHttpRequest.txt の内容が alert() されます。

XMLHttpRequest.js にもサンプルコードがありますが、HTMLを用意していないので適当に読んでください。

ここをクリックしてください

<script type="text/javascript"><!--

// --- XMLHttpRequest

if (!'XMLHttpRequest' in this) {
  this.XMLHttpRequest = (function () {
    var i, l;

    for (i = 0, l = arguments.length; i < l; i++) {
      try {
        return arguments[i];
      }
      catch (err) {}
    }
    return null;
  })(
  function () { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); },
  function () { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); });
}


// --- 非同期通信を実行する関数

function getRequest () {
  var req = new XMLHttpRequest(),
      method, url, async, data;

  if (!'open' in req) { // req.open の存在チェック
    req = null;
    return false; 
  }

  method = 'GET';                   // リクエストメソッド
  url    = './XMLHttpRequest.txt';  // リクエストURL
  async  = true;                    // 非同期通信フラグ (true = 非同期通信 / false = 同期通信)
  data   = null;                    // リクエスト先に送信するデータ (GETリクエストは送信するデータがないので null にする)

  req.open (method, url, async);                // メソッド, URL, 同期フラグ を指定する
  req.onreadystatechange = onReadystatechange;  // onreadystateイベントハンドラ属性に関数をセット

  // キャッシュを無効にする
  req.setRequestHeader('Pragma', 'no-cache');
  req.setRequestHeader('Cache-Control', 'no-cache');
  req.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');

  req.send (data);                              // データを送信する (null)


  // readystate(XMLHttpRequestオブジェクトを準備する状態) が変化した時に呼ばれる関数)
  function onReadystatechange () {

    // readyStateをチェック
    // http://www.w3.org/TR/XMLHttpRequest/#states
    if (req.readyState === 4) {                     // readyState === 4 はデータ読み込みが完了した状態

      // HTTPステータスコードをチェック
      if (req.status === 200 && req.responseText) { // HTTPステータスコード200 は「リクエスト成功」を表す
        alert(req.responseText);                    // サーバから得た内容 (文字列リテラル) を alert() する
      }
      req.abort();                                  // 通信を中断する
      req.onreadystatechange = new Function;        // onreadystatechangeイベントハンドラ属性に空のFunctionオブジェクトを代入 (イベントハンドラ属性を初期化することで、IE6のメモリリークを回避する。prototype.js の回避方法と似ているかも。)
      req = method = url = async = data = null;     // ActiveX() が生成するXMLHttpRequest互換オブジェクトをクロージャが参照を保持すると IE6 で循環参照(メモリリーク) するので、null で埋めて回避しておく
    }
  }

  function finalize () {
    req = method = url = data = async = null;
  }
}


// --- イベントリスナー関数

function clickListener (event) {
  var button = event.target || event.srcElement;

  if (button.id !== 'Test') { return; }

  getRequest ();
}

// --- イベントの定義

if (document.addEventListener) {    // for Gecko/Opera/Webkit/IE9+
  document.addEventListener('click', clickListener, false);
} else if (document.attachEvent) {  // for IE8-
  document.attachEvent('onclick', clickListener);
}

//--></script>
</head>
<body>

<p><span id="Test" class="button">ここをクリックしてください</span></p>

参考URL

W3C仕様書
XML仕様書
MDC
MSDN
その他