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

AJAX とは?

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

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

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

Step 1 – 「お願いします!」 もしくは HTTP リクエストの送り方

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

この結果、必要なクラスのクロスブラウザインスタンス (オブジェクト) を生成するには、

if (window.XMLHttpRequest) { // Mozilla, Safari, ...
    httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
    httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}

とすることになります。

(説明のために、このコードは実際に XMLHTTP インスタンスを作成するのに使用するコードよりも多少簡単にしています。より実際に近いサンプルは、この記事の step 3 を見てください。)

Mozilla ブラウザのいくつかのバージョンでは、サーバから XML mime-type のヘッダが帰ってこなかった場合に、正常に動作しないことがあります。これを何とかするためには、サーバからのヘッダが text/xml でない場合に備えて、ヘッダを上書きするメソッドを余分に呼び出すのがいいでしょう。

httpRequest = new XMLHttpRequest();
httpRequest.overrideMimeType('text/xml');

次に、リクエストに対するサーバからの応答を受け取った後に何をするかを決めなければなりません。この段階で行う必要があるのは、どの JavaScript 関数に応答を処理させるかを HTTP リクエストオブジェクトに教えることだけです。これは、オブジェクトの 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=othervalue&so=on

のようなクエリ文字列の形式である必要があります。

データを POST する必要がある場合、次の方法でリクエストの MIME タイプを変更しなければなりません。

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

そうでなければ、サーバは POST されたデータを破棄します。

Step 2 – 「はいどうぞ!」 もしくはサーバ応答の扱い方

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

httpRequest.onreadystatechange = nameOfTheFunction;

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

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

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 オブジェクトとして返します

Step 3 – 「みんなまとめて!」 - 簡単な例

さて、ここまでに紹介した方法を使って簡単な HTTP リクエストを実行してみましょう。われわれの JavaScript では test.html という名前の、"これはテストです" と書かれた HTML 文書を要求し、その内容を alert() で表示します。

<script type="text/javascript" language="javascript">
function makeRequest(url) {
    var httpRequest;

    if (window.XMLHttpRequest) { // Mozilla, Safari, ...
        httpRequest = new XMLHttpRequest();
        if (httpRequest.overrideMimeType) {
            httpRequest.overrideMimeType('text/xml');
            // この行については下の注記を参照
        }
    } else if (window.ActiveXObject) { // IE
        try {
            httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {}
        }
    }

    if (!httpRequest) {
        alert('中断 :( XMLHTTP インスタンスを生成できませんでした');
        return false;
    }

    httpRequest.onreadystatechange = function() { alertContents(httpRequest); };
    httpRequest.open('GET', url, true);
    httpRequest.send('');
}

function alertContents(httpRequest) {
    if (httpRequest.readyState == 4) {
        if (httpRequest.status == 200) {
            alert(httpRequest.responseText);
        } else {
            alert('リクエストに問題が発生しました');
        }
    }
}
</script>
<span
    style="cursor: pointer; text-decoration: underline"
    onclick="makeRequest('test.html')">
        リクエストを実行
</span>

このサンプルでは、

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

: 上のコードの httpRequest.overrideMimeType('text/xml'); という行は、Firefox 1.5 以降では、バグ 311724 で述べられているように、XMLHttpRequest によって取得されたページが妥当な XML ではない (プレーンテキストなど) 場合、JavaScript コンソールにエラーを出力します。 これは実際には正しい挙動です。この記事はこの変更に対応するためすぐに改訂されるでしょう。

注 2: リクエストを送信する先が静的な XML ファイルではなく、XML を返すコードである場合、Mozilla だけではなく Internet Explorer でもそのページを動作させたければ、いくつかのレスポンスヘッダを設定しなければなりません。Content-Type: application/xml というヘッダを設定しなければ、XML 要素にアクセスしようとしている行で IE が 'Object Expected' という Javascript エラーを投げるでしょう。また Cache-Control: no-cache というヘッダを設定しなければ、ブラウザがレスポンスをキャッシュしてリクエストを再送信しなくなるため、デバッグが「やりがいのある」ものとなるでしょう。

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

注 4: onreadystatechange にコールバック関数を登録する際、引数を渡すことはできません。このため、次のようなコードは機能しません。

httpRequest.onreadystatechange = alertContents(httpRequest); // (機能しない)

したがって、関数の登録を成功させるには、無名関数を使って間接的に引数を渡すか、httpRequest をグローバル変数として使用するのがいいでしょう。例を示します。

httpRequest.onreadystatechange = function() { alertContents(httpRequest); };  //1 (同時リクエスト)
httpRequest.onreadystatechange = alertContents;  //2 (グローバル変数)

方法 1 を使用すれば複数のリクエストを同時に処理できるのに対し、方法 2 を使用するには httpRequest をグローバル変数にする必要があります。

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

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

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

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

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

<?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 ファイルは ここ で、変更されたスクリプトは ここ で見ることができます。

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

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

タグ: 
最終更新者: ethertank,