up_l dmy up_r
dmy home dmy dmy dmy

●Ajaxでsuggest(入力補完)●

Ajaxでsuggest(入力補完)を作成する機会があったのでちょこっと紹介します。
作って理解するAjax --- No.4予測・補完インタフェースを作成:ITpro」にある
complete.js、initcompletion.jsを入手してcomplete.jsをちょっぴり改良してもっと使いやすくしようと思います。

それとXMLHTTPRequestは日本語をそのまま送信すると文字化けすることがありますので、
今回はエンコードライブラリ「Escape Codec Library(ecl.js)」も使用します。

またXMLHTTPRequestはUTF8でデータを送信するので他の文字コード環境の場合文字化けを起こします。
その場合は、Escape Codec Library(ecl.js)で適切な文字コードでエンコードした後、
サーバー側のスクリプトで「header('Content-Type: text/html; charset=EUC-JP');」等としてヘッダーで文字コードを指定してあげると文字化けを回避できます。

※また今回は補完候補リストを郵便番号と住所、補完候補リストの保存形式をテキストとして説明していきます。

■complete.jsの改良

complete.js
» 1つのテキストボックスにのみ対応
» onLoadでcomplete.jsをロードしてテキストボックスの状況を監視
というシンプルなものです。

今回アレンジする点は
» 複数のテキストボックスに対応
» 候補を選択した場合にその候補を2つのテキストボックスに反映
(例)「1000001:東京都千代田区」という候補を選択した場合に「テキストボックス1に1000001」「テキストボックス2に東京都千代田区」
» onLoadで常時監視するのではなくonFocus時にのみ監視
» 表示される候補一覧の入力した文字列部分赤で強調
という形にしたかったので、complete.jsを以下のように改良しました。

[complete.js]
------------------------------------------------------------------------------
function createXmlHttpRequest() {
    var xmlhttp = false;
  if( window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest();
  } else if(window.ActiveXObject) {
    try {
      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
  }
  return xmlhttp;
}

var oldquery = "";
var xmlhttp = 0;
var input = 0;

function peekQuery (type,vid,nid,src) {

  if (! xmlhttp) xmlhttp = createXmlHttpRequest();

  if (! xmlhttp || xmlhttp.readyState == 1 || 
      xmlhttp.readyState == 2 || xmlhttp.readyState == 3){
    return; 
  }

  var textbox = document.getElementById(vid);
  var textbox2 = document.getElementById(nid);
  var query   = EscapeUTF8(textbox.value);

  if (query == "") {
    textbox.clearCompletionItems();
    textbox2.value = "";
  } else if (oldquery != query) {
    xmlhttp.open("GET", src + "?TYPE=" + type + "&KEY=" + query, true);
    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4 && xmlhttp.status == 200
      && xmlhttp.responseText != "") {
        var ary = xmlhttp.responseText.split(/\n/);
        textbox.showCompletionItems(
          ary,
          function(n) {
            ary[n] = ary[n].replace(/<font color=red>/g,"");
            ary[n] = ary[n].replace(/<\/font>/g,"");
            var new_ary = ary[n].split(":");
            if (type == 1) {
              textbox2.value = new_ary[0];
              textbox.value = new_ary[1];
            } else {
               textbox2.value = new_ary[1];
               textbox.value = new_ary[0];
            }
            textbox.clearCompletionItems();
        oldquery = EscapeUTF8(textbox.value); } );
      }
    }
    xmlhttp.send(null)
  }

  oldquery = query;
}

function suggestOn(t,iid,oid,src) {
  var textbox = document.getElementById(iid);
  initCompletion(textbox);
  TimerID = setInterval(
    function () { peekQuery(t,iid,oid,src); },
  500);
}

function suggestOff(iid) {
  clearInterval(TimerID);
  DTimerID = setInterval(
    function () {
      var textbox = document.getElementById(iid);
      textbox.clearCompletionItems();
      clearInterval(DTimerID); },
  200);
}
------------------------------------------------------------------------------

■補完候補リストの作成

※補完候補リストのフォーマットは「○○○:△△△」という形式になります。

[post.txt]
------------------------------------------------------------------------------
1000001:東京都千代田区
1000002:東京都千代田区皇居外苑
1000003:東京都千代田区一ツ橋
・
・
・
------------------------------------------------------------------------------
※データベース(MYSQL等)を使ってリスト化すれば候補一覧の表示がスムーズになります。

■補完候補を出力するスクリプト

スクリプト自体はCGIでもphpでもなんでもよいです。
ポイントとしては
» リクエストMETHODがGETである
» GETで受取ったデータは「TYPE」と「KEY」に格納されている
という2点です。

※「TYPE」は入力した文字列の検索対象を指定した値が格納されています。

» TYPEが0だった場合はセパレータ「:」より前の文字列を対象に検索します。
(例)補完候補リストが「1000001:東京都千代田区」だった場合は「1000001側を検索」
» TYPEが1だった場合はセパレータ「:」より後ろの文字列を対象に検索します。
(例)補完候補リストが「1000001:東京都千代田区」だった場合は「東京都千代田区側を検索」

※「KEY」は入力した文字列が格納されています。

※今回は補完候補の検索にphpスクリプト(実際の検索はUNIXシェル)を使用します。

[suggest.php]
------------------------------------------------------------------------------
<?php

    $FILE = './post.txt';
    $TYPE = "";
    $KEY  = "";
    $ARY  = array();
    $CNT  = 0;

    if (isset($_GET['TYPE'])) {
        $TYPE = $_GET['TYPE'];
    }
    if (isset($_GET['KEY'])) {
        $KEY = $_GET['KEY'];
    }

    //不要な文字列の除去
    $KEY = preg_replace("/[a-zA-Z\/\;\&\|]/g","",$KEY);

    //郵便番号で検索
    if ($TYPE == 0) {
        //検索開始
        $ARY = split("\n",shell_exec("/bin/cat $FILE | /bin/grep '^$KEY'"));

        foreach ($ARY as $VAL) {
            if ($VAL != "") {
                list($NUM,$NAME) = split(":",$VAL,2);

                if (preg_match("/^$KEY/",$NUM)) {
                    //文字列にマッチした部分を赤で強調
                    $NUM = preg_replace("/^$KEY/","<font color=red>$KEY</font>",$NUM);
                    print $NUM . ':' . $NAME . "\n";
                    $CNT++;
                }

                if ($CNT == 30) { //補完候補が30件になったら終了
                    break;
                }
            }
        }
    }
    //住所で検索
    else if ($TYPE == 1) {
        //検索開始
        $ARY = split("\n",shell_exec("/bin/cat $FILE | /bin/grep ':$KEY'"));

        foreach ($ARY as $VAL) {
            if ($VAL != "") {
                list($NUM,$NAME) = split(":",$VAL,2);

                if (preg_match("/^$KEY/",$NAME)) {
                    //文字列にマッチした部分を赤で強調
                    $NAME = preg_replace("/^$KEY/","<font color=red>$KEY</font>",$NAME);
                    print $NUM . ':' . $NAME . "\n";
                    $CNT++;
                }

                if ($CNT == 30) { //補完候補が30件になったら終了
                    break;
                }
            }
        }
    }

?>
------------------------------------------------------------------------------

■ページに設置する

[HEADER]
------------------------------------------------------------------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang=ja>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<script type="text/javascript" src="initcompletion.js"></script>
<script type="text/javascript" src="complete.js"></script>
<script type="text/javascript" src="ecl.js"></script>
</head>
------------------------------------------------------------------------------

[BODY]
------------------------------------------------------------------------------
<input type="text" id="POST1" name="POST1" autocomplete="off" size="10" value=""
onFocus="suggestOn(0,'POST1','ADDR1','suggest.php');" onBlur="suggestOff('POST1');">
<input type="text" id="ADDR1" name="ADDR1" autocomplete="off" size="40" value=""
onFocus="suggestOn(1,'ADDR1','POST1','suggest.php');" onBlur="suggestOff('ADDR1');">
<br />
<input type="text" id="POST2" name="POST2" autocomplete="off" size="10" value=""
onFocus="suggestOn(0,'POST2','ADDR2','suggest.php');" onBlur="suggestOff('POST2');">
<input type="text" id="ADDR2" name="ADDR2" autocomplete="off" size="40" value=""
onFocus="suggestOn(1,'ADDR2','POST2','suggest.php');" onBlur="suggestOff('ADDR2');">
<br />
<input type="text" id="POST3" name="POST3" autocomplete="off" size="10" value=""
onFocus="suggestOn(0,'POST3','ADDR3','suggest.php');" onBlur="suggestOff('POST3');">
<input type="text" id="ADDR3" name="ADDR3" autocomplete="off" size="40" value=""
onFocus="suggestOn(1,'ADDR3','POST3','suggest.php');" onBlur="suggestOff('ADDR3');">
------------------------------------------------------------------------------

» POST*は郵便番号入力フォームを指しています。
» ADDR*は住所入力フォームを指しています。
» autocomplete="off"はブラウザがキャッシュしている過去に入力した候補一覧をOFFにします。
» onFocus="suggestOn(TYPE,THIS_FORM_ID,RETURN_SET_ID,SCRIPT);は入力フォームにカーソルが来た時にsuggestをONにします。
» TYPEに0を指定した場合は補完候補リスト中のセパレータ「:」より前の文字列を検索します。
» TYPEに1を指定した場合は補完候補リスト中のセパレータ「:」より後ろの文字列を検索します。
» THIS_FORM_IDは自身のフォームIDを指定して下さい。
» RETURN_SET_IDは補完候補を選択した際に自身のフォームと対になっているフォームIDを指定して下さい。
» SCRIPTは補完候補を出力するスクリプトのURL(URI)を指定して下さい。
» onBlur="suggestOff(THIS_FORM_ID);"は入力フォームからカーソルが離れた時にsuggestをOFFにします。
» THIS_FORM_IDは自身のフォームIDを指定して下さい。


■動作サンプル
------------------------------------------------------------------------------
郵便番号1 住所1
郵便番号2 住所2
郵便番号3 住所3
------------------------------------------------------------------------------

上記で説明したサンプルスクリプト等を以下に置いておきます。
» complete.js
» initcompletion.js
» ecl.js
» post.txt
» suggest.php
» suggest.html

▲TOP

インプラント | チョコレート | ジュース | アメリカ留学 | オーストラリア留学
dmy
low_l dmy low_r