JavaScriptでインクリメンタル検索を実装する
いよいよ本題の(?)、Ajaxインクリメンタル検索の作成を行います。
余談ですが、仕様化はフロント側(画面など)から行いますが、実装はバック側(テーブル実装など)から行うのが一般的です。Ajaxを使いこなす上でも、バック側の実装スキルが必要なのはいうまでもありません。
早速、JavaScriptによるソースコードを説明します。
XMLHttpRequestを生成する
XMLHttpRequestは、ブラウザに実装された、HTTPクライアント機能を実現するオブジェクトです。JavaScriptから、フォームを利用したHTTP通信でおなじみの、GET
やPOST
メソッドを実行し、そのレスポンスを受け取ることができます。これにより、ブラウザのリロード無しでサーバー側とデータ交換できることが、Ajaxのメリットの1つとなっています。
function createXmlHttpRequest() { if (window.XMLHttpRequest) { // Firefox,Opera,Safari,IE7 return new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE6 try { return new ActiveXObject("Msxml2.XMLHTTP"); // MSXML3 } catch(e) { return new ActiveXObject("Microsoft.XMLHTTP"); // MSXML2まで } } else { return null; } } var xmlhttp = createXmlHttpRequest();
このように、XMLHttpRequestオブジェクトの生成にはブラウザの差異を意識する必要があります。
XMLHttpRequestを利用して検索結果を取得する関数を作る
/* 抜粋 */ function query() { //XMLHttpRequestを生成する var xmlhttp = createXmlHttpRequest(); //キーワード欄への入力内容 var q = document.getElementById('q').value; var keyword = encodeURI(q); //URIエンコード xmlhttp.open("GET", "search-ajax.php?q=" + keyword, true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { //結果をdivの内容として書き換える document.getElementById("result").innerHTML = xmlhttp.responseText; } } xmlhttp.send(null) }
このコードについて解説します。
- xmlhttp.onreadystatechange = function() {
- XMLHttpRequestオブジェクトの状態が変化する度に呼び出されるイベントハンドラです。ここにイベント発生時の処理を記述します。
- if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
- readyStateは通信の状態を示します。
- 0 : uninitialized(初期状態)
- 1 : loading(読み込み中)
- 2 : loaded(読み込み完了)
- 3 : interactive(読み込んだデータ解析中)
- 4 : complete(全て完了(完了または失敗))
- statusはHTTPのステータスです。一例として、200は正常、404はNotFoundとなります。
- readyStateは通信の状態を示します。
- document.getElementById("result").innerHTML = xmlhttp.responseText;
- responseTextはサーバからのレスポンスをそのまま受け取るプロパティです。ここでは検索処理(search-ajax.php)がレスポンスしたHTMLを受け取ることができます。
ユーザのイベントを処理する
ページが読み込まれた後に、ユーザイベントの監視を開始します。
window.onload = function() { var q = document.getElementById("q"); if (q.addEventListener) { q.addEventListener("keyup", query, false) //Firefox, Opera, Safari } else { q.attachEvent("onkeyup", query); //IE } setInterval("query()", 700); }
- addEventListenerまたはattachEvent
- 要素に対するイベントを割り当てます。ここでは検索テキストボックスのonkeyupイベント発生時、query関数を呼び出すように指定しています。
- setInterval
- 指定した時間毎に繰り返し処理をするタイマーを設定します。ここでは700ミリ秒ごとにquery関数を呼び出すように指定しています。
状態に応じて検索処理の続行/中断を判断する
イベントは、サーバと通信中の場合にも発生する可能性があります。また、複数の文字で構成されるキーワードについては、全ての文字が入力されるのを待ってからサーバに問い合わせた方が、サーバの負荷軽減になります。
というわけで、先ほどのquery
関数を書き換えてみます。
var keyword_save = "~^|"; //dummy var xmlhttp = null; var baseTime = new Date(); var c = 0; function query(flg) { //キーワード欄への入力内容 var q = document.getElementById('q').value; //URIエンコード var keyword = encodeURI(q); //前回入力時刻との差(ミリ秒) var elapsed = parseInt((new Date()).getTime() - baseTime.getTime()); //基準時間の更新 baseTime = new Date(); //600ミリ秒内に次の文字が入力された場合は //キーワード入力中とみなして中断する if (elapsed < 600) { return; } //XMLHttpRequestを生成する if (!xmlhttp) xmlhttp = createXmlHttpRequest(); if (!xmlhttp || xmlhttp.readyState == 1 || xmlhttp.readyState == 2 || xmlhttp.readyState == 3) { return; } if (keyword_save != keyword) { xmlhttp.open("GET", "search-ajax.php?q=" + keyword, true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { document.getElementById("result").innerHTML = xmlhttp.responseText; } } xmlhttp.send(null) keyword_save = keyword; //キーワード保存 } }
最後に
全てのサンプルはこちらからダウンロードして下さい。
Ajaxアプリケーションの基本的な流れを把握できたでしょうか?JavaScriptフレームワークを利用すれば、もっと少ないコードで実装することができるようになります。また、サーバサイドはPHPに限らず、JavaやRubyで構築しても構いません。
Ajaxはこのほかにも、見栄えを良くしたり、外部のサービスを利用することができたり、非常に強い力を発揮します。これらは、ユーザが対象のアプリケーションをより使いやすくするために用いるべきです。
「Ajaxが実装できない」のは良くありませんが、システムが複雑になる傾向にあるため、濫用も良くないと考えています。
読者の皆様が、この記事を通じて積極的に、そしてユーザのためにAjaxを駆使していただければ幸いです。
参考資料
- 『Ajaxイン・アクション』(Dave Crane 著、Eric Pascarello 著、Darren James 著、柏原 正三 著、株式会社はてな 著、網代 淳 著、星 睦 著、インプレス、2006年6月)