XMLHttpRequest オブジェクトの利用方法

Web サービスが広く使われるようになり、不都合に感じてくるのがいちいちページを再ロードする必要があるということです。これらを解消することができないか?ということで最近注目されたのが XMLHttpRequest です。これはもともと Internet Explorer 5 の Active X のオブジェクトとしてインプリメントされました。それを Mozilla 1.0 や Safari 1.2 でも採用されることになりました。

オブジェクトの生成

まずは、オブジェクトを生成するところから始まります。オブジェクトは、Mozilla, Safari では XMLHttpRequest を Internet Explorerでは XMLHTTP をActiveX にて生成します。オブジェクトとを生成する関数を作成してみます。

// XMLHttpRequestオブジェクト生成
function createHttpRequest() {
    // for ie7, Mozilla, FireFox, Opera8
    if (window.XMLHttpRequest) {
        try {
            return new XMLHttpRequest();
        } catch (e) {
            return false;
        }
    }
    // for ie5, ie6
    else if (window.ActiveXObject) {
        try {
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                return false;
            }
        }
    }
    else {
        return false;
    }
}

メソッド

オブジェクトを生成した後は、メソッドを利用して必要なコントロールを行います。

メソッド 概要
abort() 現在のリクエストを停止
getAllResponseHeaders() すべてのヘッダーを返す(ラベルと値)
getResponseHeader("headerLabel") 指定したヘッダーのラベルの値を返す
open("method", "URL"[, asyncFlag[, "userName"[, "password"]]]) メソッドやURLなどを割り当てる。asyncFlag が true で非同期通信、falseで同期通信となる。
send(content) リクエストを送信。文字列や DOMオブジェクトを指定可能。
setRequestHeader("label", "value") ラベルと値でついになったヘッダーを送信。

open() および send() が最も利用するメソッドです。

まず、open() によりサーバー側で実行されるプログラムの設定を行います。はじめの引数で、データをサーバーに渡す方法を指定します。URLを利用してデータを送る場合"GET" を利用します。"GET"は、簡単にデータをサーバーに送ることができますが、URIエンコードをしておく必要があることとサイズを全体で 512バイト以内に抑える必要があります。大きなデータを扱う場合には"POST"を利用します。"POST"は、FORMで 指定されたデータしか扱うことができません。2番目に引数である URL でサーバ側で実行されるプログラムを指定します。 3番目に引数は、通信を非同期で行うか同期で行うかを指定します。trueが非同期通信、falseが同期通信となります。 デフォルトは非同期通信です。非同期通信は、send()メソッドが実行された後にサーバで実行されるプログラムのレスポンスを待たずに次の処理を続けます。サーバーで実行されたプログラムのレスポンスは onreadystatechange イベントをチェックすることで処理します。

var objReq=false;
// 送信関数
// @param callback 受信時に起動する関数名
// @param data     送信するデータ
// @param method   "POST" / "GET"
// @param url      実行するプログラムのURL
// @param async    true:非同期 / false:同期

function sendRequest(data, method, url, async) {
    // XMLHttpRequestオブジェクト生成
    objReq = createHttpRequest();
    if (objReq == false) {
        return null;
    }
    objReq.onreadystatechange = procReqChange;
    if (method == 'GET') {
        url = url + encodeURI(data);
    }
    objReq.open(method, url, async);
    if (method == 'POST') {
        objReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    }
    objReq.send(data);
}

プロパティ

リクエストを送ったあとは、いくつかのプロパティを監視することでサーバー側で実行されたプログラムのレスポンスを確認できます。

プロパティ 概要
onreadystatechange 状態が変更された際のイベントハンドラを指定。
readyState オブジェクトの状態:
0 => uninitialized
 

オブジェクトが作成されたが、初期化されていない(openメソッドが呼び出されていない)。

1 => loading
  オブジェクトが作成されたが、sendメソッドが呼び出されていない。
2 => loaded
 

メソッドが呼び出され、状態とヘッダーが使用可能になったが、使用可能になっていない。

3 => interactive
  一部のデータを受信。responseBodyとresponseTextを呼び出して、現時点の部分的な結果を取得可能。
4 => complete
  すべてのデータを受信し、responseBodyとresponseTextで完全なデータを取得可能。
responseText サーバーから文字列形式のデータが返る。文字エンコーディングはブラウザによって異なる。
responseXML サーバーからDOM形式のデータが返る。
status

サーバからの数値によるリターンコード。404 は "Not Found"、200 は "OK"。

statusText サーバからの文字列によるリターンメッセージ。

readyStateプロパティを利用することでオブジェクトの状態を確認できる。サーバー側のプログラムが完了したことを表す値は 4 となります。また、サーバー側のプログラムがどのような状態で完了したかは statusプロパティを利用することで確認できます。HTTPなので、status200が成功となります。失敗していた場合、statusTextプロパティを利用してエラーの内容を確認できます。サーバーからのデータは、responseTextまたはresponseXMLプロパティーを通して返ります。

// 受信時に起動するコールバック関数
function procReqChange() {
    // 状態
    if (objReq.readyState == 4) {
        // ステータス
        if (objReq.status == 200) {
            // 成功!
            alert("受信しました: " + objReq.responseText);
        } else {
            // 何か不具合が発生?
            alert("問題が発生しました: " + objReq.statusText);
        }
    }
}

サンプル

上記をふまえて簡単なプログラムを実行してみます。とても簡単なもので、ボタンを押すと1秒後にメッセージが切り替わるものです。

<html>
<head>
<title>XMLHttpRequest test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<script language="JavaScript">
<!--
// create XMLHttpRequest object
function createHttpRequest() {
    // for ie7, Mozilla, FireFox, Opera8
    if (window.XMLHttpRequest) {
        try {
            return new XMLHttpRequest();
        } catch (e) {
            return false;
        }
    }
    // for ie5, ie6
    else if (window.ActiveXObject) {
        try {
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                return false;
            }
        }
    }
    else {
        return false;
    }
}

var objReq;
function sendRequest(data, method, url, async) {
    objReq = createHttpRequest();
    if (objReq == false) {
        return null;
    }
    objReq.onreadystatechange = procReqChange;
    if (method == 'GET') {
        url = url + encodeURI(data);
    }
    objReq.open(method, url, async);
    if (method == 'POST') {
        objReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    }
    objReq.send(data);
}

function procReqChange() {
    if (objReq.readyState == 4) {
        if (objReq.status == 200) {
            var obj = document.getElementById("maintext");
            obj.innerHTML = objReq.responseText;
        } else {
            alert("ERROR: " + objReq.statusText);
        }
    }
}
//-->
</script>
</head>

<body>
<form>
<input type=button value="get message" 
  onClick="sendRequest('', 'GET', 'mess.php', true); return false;">
</form>
<div id="maintext">message here</div>
</body>
</html>
<?php
sleep(1);
echo 'hello, world!';
?>

"get messgae"ボタンをクリックすると、1秒後にメッセージが変わります。

sendRequestの4番目の引数を true から false に変更すると同期通信となります。どう違うかは実際に試してみるとわかります。