JavaScriptのXMLHttpRequestによる非同期通信
以前の記事「JavaScriptと絵文字(サロゲートペア)でハマる」でXMLHttpRequestに触れたため、すっかり投稿した気になっていましたが、JavaScriptによる非同期通信についてちゃんと書いていなかったため、自分用のメモがてら投稿しておきます。
JavaScriptとPHP等の通信処理はもはや珍しくもない
今からほんの10年くらい前まではJavaScriptというだけで嫌われ、サーバーサイドのCGIだけで処理するのが安全で美しいとされてきました。
例えば入力フォームでの入力値チェック。
数値しか入力してはいけないところにそれ以外の文字列が入れられていないかどうか、メールアドレスを入力する欄で全角英数を入力してしまってはいないか等、入力フォームと入力値チェックはセットです。
古い実装ではそれらはすべてPHP等のCGIで実行され、チェックの結果、入力値に問題があればエラーを表示する。つまり、入力フォームからのPOST→CGIでの入力チェック→エラーだったらもう一度入力フォームへ戻す、という通信量的には大変無駄なことをしていたわけです。
いや現実問題として、社内で使われる業務系のWebシステムなんて未だに大半がこのやり方じゃないでしょうか。通信量や入力側の使い勝手を考慮しないなら、入力チェックのロジックがサーバーサイドで完結するというメリットもありますから。
ところが、数年前から Ajax! Ajax! と突然JavaScriptの非同期通信がもてはやされるようになりました。
最初は正直、え、いまさらなのか、と戸惑いました。だってもともとJavaScriptにはXMLHttpRequestという非同期通信のためのオブジェクトがあり、ぼくが知る限り、今から19年前にもなる2000年には実装されていたものですから。
ただ、実装当時はIEでしか使えなかったり、ActiveX用の技術と考えられていたため、普及には至らず、クロスブラウザによる対応が含まれるjQueryの台頭により、まるで新しい技術かのようにあらためて注目されることになりました。
こうして有名な技術になることには大きなメリットがあります。個人の開発においては好きに非同期通信を使っていたぼくですが、開発の受注案件ともなると顧客からの要求に答えなければならず、JavaScriptは極力使わないこと、なんていう目を疑う要件があったりしたものなんです。
それが今ではAjaxやjQueryという単語で通じちゃう。使用が許されちゃう。
更にはモダンブラウザが主流になり、IE11排除の流れも進んだおかげで、そのjQueryすら排除して、元々のXMLHttpRequestを直接使うことも許される風潮になってきました。
いやぁ、ここまで長かった。せっかく高機能なJavaScriptをようやくちゃんと活かせる環境が整ってきました。
最初の例に出した入力フォームなら、onchangeイベントで非同期通信をして、ユーザーがほかの入力をしている間にそっと入力チェックすることも可能になったわけです。郵便番号を入力して[検索]ボタンを押してもらう、とかそういう古い実装に頼らなくても良いのです。良い時代になりました。
XMLHttpRequestによる非同期通信の基本
前置きのほうが長くなりましたが、XMLHttpRequestの簡単な使用例。
function asyncRequest(url)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
//ここに完了時の処理を書く
//サーバーサイドからの返り値はthis.responseTextとかでもらう
}
}
xhr.send(null);
}
■解説
XMLHttpRequestオブジェクトを作り、open関数でURLを開き、onreadystatechangeに通信完了時に実行したい関数をセットする、という流れです。尚、open関数の引数 true は非同期通信する、という意味なので、同期通信がしたければ false を指定します。
それから、今回はGETリクエストなのでxhr.sendの引数はnull。フォームデータをPOSTしたい場合などはsend関数にformオブジェクトを渡します。
ちなみに最近知ったのですが、onreadystatechangeで通信完了をチェックするやり方は古いそうで…。最近はonloadだけでも良いみたいです。
■具体例
function asyncRequest(url)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function () {
//ここに完了時の処理を書く
//サーバーサイドからの返り値はthis.responseTextとかでもらう
}
xhr.send(null);
}
動作的には同じようですし、こちらのほうが見やすくて良いですね。
XMLHttpRequestで既存フォームをPOSTする例
■HTMLを含めた具体例
<html>
<body>
<form method="post" id="f1">
<input type="text" name="p1" value="ほにゃらら">
<input type="button" value="送信" onclick="asyncPost('f1', 'post.php');">
</form>
<script>
function asyncPost(formid, url)
{
var formElement = document.getElementById(formid);
var formData = new FormData(formElement);
xhr = new XMLHttpRequest();
xhr.open("POST", "post.php", true);
xhr.onload = function () {
//ここに完了時の処理を書く
}
xhr.send(formData);
}
</script>
</body>
</html>
■解説
xhr.openの引数がGETからPOSTになったのと、getElementByIdで取得したformオブジェクトをFormData化してsend関数に渡してあげるだけなので簡単ですね。
XMLHttpRequestでPOSTする際、動的にformデータに項目を追加する例
■HTMLを含めた具体例
<html>
<body>
<form method="post" id="f1">
<input type="text" name="p1" value="ほにゃらら">
<input type="button" value="送信" value="asyncPost('f1', 'post.php');">
</form>
<script>
function asyncPost(formid, url)
{
var formElement = document.getElementById(formid);
var formData = new FormData(formElement);
formData.append("p2", "ほにゃらら2");
xhr = new XMLHttpRequest();
xhr.open("POST", "post.php", true);
xhr.onload = function () {
//ここに完了時の処理を書く
}
xhr.send(formData);
}
</script>
</body>
</html>
■解説
FormData化したデータにappend関数で、「name="p2" value="ほにゃらら2"」な項目を作り、そのFormDataをsend関数に渡しています。
何なら既存のフォームを用意せずに var formData = new FormData(); として、新規にフォームデータを作っても構いません。
実行結果の処理
JavaScriptからpost.phpを呼び出したとして、その返り値をどうするかについてですが、それは特に明確な決まりはありません。
XMLHttpRequestなんて名前が付いているのでPHP側はXMLで返さなければいけないのかと思われるかもですが、別にそんなことはないです。
以前の記事「PHPでjson_encode/json_decodeする例」で紹介したようにPHPの連想配列をjson_encodeでJSONデータにしてechoしてあげるだけでも構いません。
その場合、JavaScript側は this.responseText を JSON.parse して使うだけです。
■PHP側の例
<?php
$response['result'] = -1;
$response['msg'] = '入力内容にエラーがあります';
echo json_encode($response, JSON_UNESCAPED_UNICODE);
?>
■JavaScript側の例
xhr.onload = function () {
//ここに完了時の処理を書く
var response = JSON.parse(this.responseText);
if (response.result != 0) {
alert(response.msg);
}
}
まとめ
- JavaScriptとサーバーサイドの非同期通信はXMLHttpRequestで簡単に実装できる
- 最近のモダンブラウザはもちろんのことIE11でも問題なく動作するのでわざわざjQueryに頼る必要もない
- GETはもちろんのこと、POSTでも既存フォームをそのまま投げたり、動的にフォームデータを書き換えてからPOSTする等、柔軟な対応ができる