Ajaxの実装 (検索サジェスト)

目標

本記事の目標は、以下のようにAjax通信による検索サジェストを実装することです。

環境

Ajaxについて

AJAX(Asynchronous JavaScript And XML)は、JavaScriptを使って、クライアント側で非同期通信を実現するためのWeb開発技術群を指す言葉です。非同期通信とAjaxについては、“AJAXとは (そして非同期通信とは)?”に詳しく記載しています。以下では、Ajaxの仕組みについて簡単に説明しています。

Ajaxの仕組み

Ajaxでは、「サーバーへのリクエスト送信と受信」「送信、受信した内容をブラウザ側に伝える」という2つの機構を分離しています。そのため、リクエスト待ちの間も、ブラウザ上で操作を続けることができます。

Ajaxにおいて、サーバーとクライアントの間を取り持ち、やりとりを管理する機構をAjaxエンジンと呼びます。

方法

Ajaxにおけるデータフロー

XMLHttpRequest()オブジェクトが、HTTPリクエストを送信し、レスポンスの受信を検知して、レスポンスを反映させます。

1. ブラウザのUIで、データ更新の要求を受け取る。

2. JavaScriptでXMLHttpRequest()オブジェクトを作成し、通信の完了を検知できるようにする。通信完了時の処理も先に設定。

3. XMLHttpRequest()を使ってHTTPリクエストを送信

4. レスポンスとしてXMLやJsonを受け取る

5. 受け取ったデータをブラウザ上に反映

Ajaxの実装

データベースは、“XAMPPでMaria DBのデータベースを作る”の手順に沿って作成しました。この程度のサイズであれば、先に全て取得した方が簡単なのですが、実際は大規模なデータベースがあると想定します。

データベース名: profile
テーブル名: user カラム(id, name, age, icon)

1. 検索フォームの作成

<form><input></input></form>タグで、検索フォームを作成します。

index.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>test</title>
</head>
<body>
    <form action="">
        <label>Name:</label>
        <input id="input-area" type="text" name="word" list="name-list" autocomplete="off" /><input type="submit" value="Search" />
        <datalist id="name-list">
        </datalist>
    </form>
</body>
</html>

本来は、submit時に何らかのmehod(POST, GETなど)を実行し、ページ遷移をして結果を表示しますが、今回は検索サジェストの部分のみ作成します。

<datalist></datalist>に要素を追加することで、サジェストを表示します。

2. JavaScriptでXMLHttpRequest()オブジェクトを作成、リクエストを送信

index.php 内

<script>
    var input_area = document.getElementById('input-area')
    input_area.addEventListener("input", search);

    function search(){
        var ajax =  new XMLHttpRequest();

        var word = input_area.value;
        ajax.open('GET', 'search.php?q='+ word);

        ajax.onreadystatechange = function(){
            if(ajax.readyState == 4){
                var suggest = document.getElementById("name-list");
                var result_json_str = ajax.responseText;
                var name_list_tags = "";
                if(result_json_str != ""){
                    var result_json_obj = JSON.parse(result_json_str);
                    for(k in result_json_obj){ // get keys
                        var item = result_json_obj[k];
                        name_list_tags += '<option value="' + item['name'] + '">';
                    }
                }
                suggest.innerHTML = name_list_tags;
            }
        }
        ajax.send();
    }
</script>

以下は、上記コードの詳細です。

inputに値が入力(削除)された際に、関数searchを呼び出す。

var input_area = document.getElementById('input-area')
input_area.addEventListener("input", search);

function search()内で、XMLHttpRequestオブジェクトを作成します。

var ajax =  new XMLHttpRequest();

XMLHttpRequest.open()メソッドでリクエストを作成します。引数asyncで、同期、非同期を決定することができます。デフォルトでは、非同期通信を行います。

今回は、リクエストメソッドGETを使用し、リクエスト先はsearch.php(後述)です。?q=+ wordで、入力されたwordをqという名前で渡しています。

var word = input_area.value;
ajax.open('GET', 'search.php?q='+ word);

XMLHttpRequest.onreadystatechangeで、通信状態に変化があった時に実行するコールバック関数を登録することができます。XMLHttpRequest.readyStateによって、場合分けが可能です。

readyState valuestate
0UNSENT
1OPENED
2HEADERS_RECEIVED
3LOADING
4DONE

今回は、通信が完了したとき(DONE)、XMLHttpRequest.responseTextで表示する名前のリストをJson形式で受け取り、それをフォームの<datalist></datalist>内に順番に追加しています。
PHPとJavaScriptでのJSONデータの受け渡しについては、“PHPでJsonデータを作成する方法”をご覧ください。

ajax.onreadystatechange = function(){
    if(ajax.readyState == 4){
        var suggest = document.getElementById("name-list");
        var result_json_str = ajax.responseText;
        var name_list_tags = "";
        if(result_json_str != ""){
            var result_json_obj = JSON.parse(result_json_str);
            for(k in result_json_obj){ // get keys
                var item = result_json_obj[k];
                name_list_tags += '<option value="' + item['name'] + '">';
            }
             
        suggest.innerHTML = name_list_tags;
    }
}

リクエストの送信は、XMLHttpRequest.send()で行います。

ajax.send();

3. phpでデータベースと通信

search.php では、検索語を受け取ってデータベースと通信を行います。

search.php

<?php

$dsn = 'mysql:host=localhost;dbname=profile';
$username = 'root';
$password = '*******';

if ($_GET) {
    try {
        $query=$_GET["q"];
        $dbh = new PDO($dsn, $username, $password);
        if($query==""){
            echo "";
        }
        else{
            $sql ="select * from user where name like '".$query."%'";
            $sth = $dbh->prepare($sql);
            $sth->execute();
            $result = $sth->fetchAll();
            $names = [];
            if($result){
                foreach ($result as $row) {
                    $names += array($row['id'] => array("name" => $row['name'], "age" => $row['age'], "icon" => $row['icon']));
                }
                echo json_encode($names);
            }
            else{
                echo "";
            }
        }
    }catch (PDOException $e) {
        echo  "";
        exit();
    }
}
?>

以下は、上記ソースコードの詳細です。

データベースに接続する際には、PDOを使用します。(詳細: XAMPPでPHPを使ってMySQLにアクセスする)

$dsn = 'mysql:host=localhost;dbname=profile';
$username = 'root';
$password = '*******';

if ($_GET) {
    try {
        $query=$_GET["q"];
        $dbh = new PDO($dsn, $username, $password);

SQLのSELECT文 WHERE LIKE句を使って、検索語に先頭が一致するデータを取得します。(関連: PHPで検索フォームを作成する)

$sql ="select * from user where name like '".$query."%'";
$sth = $dbh->prepare($sql);
$sth->execute();
$result = $sth->fetchAll();

for文で取得したデータをたどり、Json形式のデータを生成します。echoで出力した内容を、JavaScriptのXMLHttpRequest.responseTextで受け取ることができます。(関連: PHPでJsonデータを作成する方法)

$names = [];
if($result){
    foreach ($result as $row) {
        $names += array($row['id'] => array("name" => $row['name'], "age" => $row['age'], "icon" => $row['icon']));
    }
    echo json_encode($names);
}

ソースコード全体

ソースコード全体は、https://gist.github.com/s-nako/dfd7bb90bcbd64f7a34b76ae339ab26bにあります。

結果

入力途中でサジェストが表示されるようになりました。

補足

このような検索では、一度取得したデータは、再利用することができます。(例えば、jだけの入力で[‘Jack Road’, ‘John Mark’, ‘John Perrill’]と出たら、ja, jb, jc…は、そこから探索すれば良いことになります)。

検索結果の上位を表示したり、毎回取得し直す必要があれば、今回のような方法になります。