MDN wants to learn about developers like you: https://qsurvey.mozilla.com/s3/MDN-survey

この記事は AJAX の基礎の概観と、入門のための二つの実践的なサンプルを示します。

AJAX とは?

AJAX は Asynchronous JavaScript and XML の頭文字を取ったものです。これは一言で言えば、非標準の XMLHttpRequest オブジェクトを使用してサーバーサイドスクリプトとやり取りすることを指します。AJAX は、XML や HTML、テキストファイルさえ含む様々なフォーマットの情報を送受信することができます。しかしながら、AJAX の特徴の中で最も魅力的なのは、こういったことすべてをページを再読み込みすることなく「非同期」に実行できるというところです。これにより、ユーザーイベントによってページを部分的に更新することができます。

ここで重要な機能は次の 2 つです。

  • ページを再読み込みすることなくサーバーにリクエストを送る
  • XML 文書をパースして利用する

Step 1 –  HTTP リクエストの送り方

JavaScript からサーバーに HTTP リクエストを送るためには、この機能を提供するオブジェクトのインスタンスが必要になります。これがXMLHttpRequest の登場する場所です。このクラスは、もともとは Internet Explorer で  XMLHTTP という名前の ActiveX オブジェクトとして導入されたものです。その後、Mozilla や Safari やその他のブラウザがこれに追随し、Microsoft のオリジナルの ActiveX オブジェクトのメソッドやプロパティをサポートする XMLHttpRequest オブジェクトを実装しました。
いっぽう、 Microsoft も同様に XMLHttpRequest を実装しました。

// 古い互換コードで、もう必要ありません。
if (window.XMLHttpRequest) { // Mozilla, Safari, IE7+ ...
    httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 6 and older
    httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
注: 説明のために、このコードは実際に XMLHTTP インスタンスを作成するのに使用するコードよりも多少簡単にしています。より実際に近いサンプルは、この記事の step 3 を見てください。

リクエストを送ったら、応答を受け取った後に何をするかを決めなければなりません。この段階で行う必要があるのは、どの JavaScript 関数に応答を処理させるかを XMLHttp リクエストオブジェクトに教えることだけです。これは、オブジェクトの onreadystatechange プロパティに、使おうとしている JavaScript 関数の名前をこのように設定することで行えます。

httpRequest.onreadystatechange = nameOfTheFunction;

このとき、関数名の後に括弧やパラメータが必要ないことに注意してください。それは、実際にそれを呼ぶのではなく単純に関数の参照を渡しているからです。また、関数名を設定するのではなく、以下のように関数や応答を処理する動作をその場で定義するという JavaScript の機能 (「無名関数」と呼ばれる) を利用することもできます。

httpRequest.onreadystatechange = function(){
    // 処理を行う
};

次に、応答を受け取った後に何をするかを宣言したら、あとは実際にリクエストを投げるだけです。これは、次のように HTTP リクエストクラスの open()send() メソッドを呼び出すことで行えます。

httpRequest.open('GET', 'http://www.example.org/some.file', true);
httpRequest.send(null);
  • open() の第 1 引数はサーバーでサポートされている HTTP リクエストメソッド、つまり、GET、POST、HEAD やその他のメソッドのなかで利用したいものになります。HTTP 標準に準拠するためにメソッド名は大文字で入れてください。そうでなければ、いくつかのブラウザ (Firefox など) ではリクエストを送信しません。利用可能な HTTP リクエストメソッドに関しての詳細情報については W3C 仕様 を参照してください。
  • 第 2 引数は、リクエストを送るページの URL です。セキュリティ上の問題から、他のドメインのページを読むことはできません。つまり、すべてのページに対して同一のドメイン名を利用しないと、open() を呼び出したときに「権限エラー」を受け取ることになるということです。よくある落とし穴は、サイトに domain.tld でアクセスしながら、www.domain.tld でページを読み込もうとすることです。
  • 第 3 引数は、リクエストを非同期に送るかどうかを示します。もし、TRUE であれば、JavaScript 関数の動作は、サーバーから応答が返らなくても続けられます。これは、AJAX の A にあたります。

send() メソッドのパラメータには、サーバーに POST で送る場合、あらゆるデータを入れることができます。このデータは、サーバーがパースできるフォーマットで送り、

"name=value&anothername="+encodeURIComponent(myVar)+"&so=on"

のようなクエリ文字列か、 multipart/form-data, JSON, XML などのほかの形式でなければなりません。

データを POST する必要がある場合、次の方法でリクエストの MIME タイプをセットしなければなりません。例えば send() で送るフォームのクエリーストリングには、事前に下記の処理をしておきます。

httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

Step 2 – サーバー応答の扱い方

サーバーへリクエストを送る前に、次のように応答を扱う JavaScript 関数の名前を設定したことを思い出してください。

httpRequest.onreadystatechange =nameOfTheFunction;

では、この関数で何をすべきかを見て行きましょう。最初に、この関数ではリクエストの状態を調べる必要があります。ステータス値が 4 であるなら、サーバーからの応答が完全に返ってきており、処理を進められることを意味します。

if (httpRequest.readyState === XMLHttpRequest.DONE) {
    // 全てが問題ない状態で、応答も返ってきています
} else {
    // まだ準備ができていません
}

readyState の値のリストは XMLHTTPRequest.readyState に文書化されていて、以下のようになります。

  • 0 (初期化されていません)か(リクエストは初期化されていません)
  • 1 (読み込み中です)か(サーバー接続が確立されました)
  • 2 (読み込みは完了しました)か(リクエストが受け付けられました)
  • 3 (双方向に扱えます)か(リクエストの処理中)
  • 4 (すべて完了しました)か(リクエストが完了して応答が準備できています)

次に、HTTP サーバーの 応答コードを調べます。帰ってくる可能性のあるコードはW3C のサイト にリストされています。われわれの目的にとっては、 200 OK が返ってきているかどうかのみが重要です。

if (httpRequest.status == 200) {
    // 完璧です!
} else {
    // 何らかの問題が発生しています。
    // たとえば、404 (ページは存在しません) や、500 (サーバー内部エラー)
    // などが返ってきているでしょう
}

ここで、リクエストの状態とレスポンスの HTTP ステータスコードの両方が確認できたので、サーバーから返ってきたデータに対して好きなことを実行できるようになりました。このデータにアクセスするには 2 通りの方法があります。

  • httpRequest.responseText – サーバーの応答をテキスト文字列として返します
  • httpRequest.responseXML – サーバーの応答を JavaScript DOM 関数で扱える XMLDocument オブジェクトとして返します

Note that the steps above are valid only if you used an asynchronous request (the third parameter of open() was unspecified or set to true). If you used a synchronous request you don't need to specify a function, but this is highly discouraged as it makes for an awful user experience.

Step 3 –  簡単な例

さて、ここまでに紹介した方法を使って簡単な HTTP リクエストを実行してみましょう。われわれの JavaScript では test.html という名前の、"これはテストです" と書かれた HTML 文書を要求し、その内容を alert() で表示します。注意として、この例では vanilla JavaScript を使っています — jQuery は入っていません。また HTML, XML, PHP ファイルは同じディレクトリーに置かれています。

<button id="ajaxButton" type="button">リクエストを実行</button>

<script>
(function() {
  var httpRequest;
  document.getElementById("ajaxButton").addEventListener('click', makeRequest);

  function makeRequest() {
    httpRequest = new XMLHttpRequest();

    if (!httpRequest) {
      alert('中断 :( XMLHTTP インスタンスを生成できませんでした');
      return false;
    }
    httpRequest.onreadystatechange = alertContents;
    httpRequest.open('GET', 'test.html');
    httpRequest.send();
  }

  function alertContents() {
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
      if (httpRequest.status === 200) {
        alert(httpRequest.responseText);
      } else {
        alert('リクエストに問題が発生しました');
      }
    }
  }
})();
</script>

このサンプルでは、

  • ユーザーがブラウザで「リクエストを実行」をクリックする
  • イベントハンドラーから makeRequest() 関数が呼び出される
  • リクエストが作成され、(onreadystatechange) により alertContents() への処理引継ぎが設定される
  • alertContents() では、応答が返ってきていて問題無いかを確認した後、test.html ファイルの中身を alert() で表示します。

: リクエストを送信する先が静的な HTML ファイルではなく、XML を返すコードである場合、Internet Explorer にレスポンスヘッダを設定しなければなりません。Content-Type: application/xml というヘッダを設定しなければ、XML 要素にアクセスしようとしている行で IE が 'Object Expected' という Javascript エラーを投げるでしょう。

注 2: Cache-Control: no-cache というヘッダを設定しなければ、ブラウザがレスポンスをキャッシュしてリクエストを再送信しなくなるため、デバッグが「やりがいのある」ものとなるでしょう。 別の GET パラメーター、タイムスタンプやランダムな数字のようなものも追加できます (キャッシュをバイパスするを見てください)

注 3: httpRequest 変数をグローバルに使用すると、makeRequest() を呼び出す関数がお互いにこの変数を上書きしあうため、競合状態が発生します。 httpRequest 変数を関数ローカルで宣言して alertContent() 関数に渡すようにすれば競合状態は発生しません。

Web サーバーのダウンなどの理由で通信エラーが発生した場合、onreadystatechange メソッドで status 変数にアクセスしようとすると例外が発生します。この問題を防ぐため、if...then 文は必ず try...catch で囲むようにしてください:

function alertContents(httpRequest) {
    try {
        if (httpRequest.readyState == XMLHttpRequest.DONE) {
            if (httpRequest.status == 200) {
                alert(httpRequest.responseText);
            } else {
                alert('リクエストに問題が発生しました');
            }
        }
    } catch( e ) {
        alert('例外を捕捉: ' + e.description);
    }
}

Step 4 – 「X-ファイル」 もしくは XML レスポンスの扱い方

前の例では、HTTP リクエストへの応答を受け取った後、リクエストオブジェクトの responseText プロパティを用いて、それに含まれている test.html の中身を取得しました。では、次に responseXML プロパティのほうを試してみましょう。

はじめに、あとでサーバーに要求する妥当な XML 文書を作成します。test.xml ファイルの中身は以下のようなものです。

<?xml version="1.0" ?>
<root>
    I'm a test.
</root>

スクリプトでは、リクエスト送出を以下のように変更します。

...
onclick="makeRequest('test.xml')">
...

そして、alertContents() では、alert(httpRequest.responseText); としている行を以下のように変更します。

var xmldoc = httpRequest.responseXML;
var root_node = xmldoc.getElementsByTagName('root').item(0);
alert(root_node.firstChild.data);

このコードでは、responseXML から XMLDocument オブジェクトを取得し、DOM メソッドを利用して XML 文書に含まれるデータにアクセスしています。 このtest.xml ファイルは ここ で、変更されたスクリプトは ここ で見ることができます。

Step 5 – データを処理する

最後に、データをサーバーに送って応答を受けましょう。 JavaScript はここでダイナミックなページ test.php にリクエストし、このページは送ったデータを受けて「計算した」文字 - "Hello, [user data]!" - を返し、これを alert() します。

まずは HTML にテキストボックスを追加してユーザーが名前を入れられるようにします:

<label>Your name: 
  <input type="text" id="ajaxTextbox" />
</label>
<span id="ajaxButton" style="cursor: pointer; text-decoration: underline">
  Make a request
</span>

イベントハンドラーに、テキストボックスからユーザーデータを取得してサーバーサイドスクリプトのURLと一緒に makeRequest() に送るような行も追加します:

  document.getElementById("ajaxButton").onclick = function() { 
      var userName = document.getElementById("ajaxTextbox").value;
      makeRequest('test.php',userName); 
  };

makeRequest() を編集してユーザーデータを受け取ってサーバーに渡すようにします。リクエストメソッドは GET から POST に変更し、データを httpRequest.send()呼び出しのパラメーターとして入れます:

function makeRequest(url, userName) {

    ...

    httpRequest.onreadystatechange = alertContents;
    httpRequest.open('POST', url);
    httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    httpRequest.send('userName=' + encodeURIComponent(userName));
  }

alertContents() 関数はステップ 3 と同じように書かれて、サーバーが計算された文字列を返していたら、alert するようにします。しかし、サーバーが計算された文字列とオリジナルのユーザーデータの両方を返していたらどうでしょう?ユーザーがテキストボックスに "Jane" とタイプしていたら、サーバーの応答はこのようになります:

{"userData":"Jane","computedString":"Hi, Jane!"}

このデータを alertContents(),内で使うには、単に responseText をalert することはできず、これを parse して、求めるプロパティの computedString をalertします:

function alertContents() {
  if (httpRequest.readyState === XMLHttpRequest.DONE) {
    if (httpRequest.status === 200) {
      var response = JSON.parse(httpRequest.responseText);
      alert(response.computedString);
    } else {
      alert('There was a problem with the request.');
    }
  }
}

test.php には下記が入ります:

$name = (isset($_POST['userName'])) ? $_POST['userName'] : 'no name';
$computedString = "Hi, " . $name;
$array = ['userName' => $name, 'computedString' => $computedString];
echo json_encode($array);

DOM メソッドについてより詳しくは、Mozilla での DOM の実装 の文書を参照してください。

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

 最終更新者: Uemmra3,