読者です 読者をやめる 読者になる 読者になる

hiroshi akutsuの日記

主にプログラミング関係のこと

Ajax【最後にonkeyupされてからn秒間次の入力が無かった場合にインクリメンタルサーチを実行】

インクリメンタル検索は、テキストボックスとかで文字を入力すると勝手にテキストボックスの下とかに表示される入力候補みたいなやつ。

これをwebで実装しようと企みました。(すでによくありますが)

環境は
クライアント:javascript(jqueryライブラリを使用します)
サーバー:php(今回は特に記載しません)


以降はjqueryをベースに書いていきます。

どのようにインクリメンタル検索を実装しているのか色々調べていると、IEFirefoxのkeyupイベントの実行タイミングが違うからkeypressを使用するとか、focus中にsetIntervalで一定間隔で実行とか、エンターが押されたタイミングで実行するとかあったけど、自分がやりたかったのはこれ。

「キーが離されてからn秒間次のキーの入力が無かった場合に検索実行」

keyupの都度検索実行していたのではサーバーの負荷が心配。
keyupやkeypressを使い分けたり、keycodeを取ってくる処理は面倒だし今後のブラウザの仕様の変更によっては調べた努力が烏有と化す。
setIntervalを用いた一定間隔で実行するのはfocusの処理、blur時のclearIntervalの処理、focus放置プレーに備えて検索クエリの文字の変化が無かった場合は問い合わせないなどの処理が面倒くさい。

要は面倒くさがりなんです。。。



なのでちょっとラグがありますが、シンプルそうな上記方法でやろうと思います。

タイピングが早い人だと1秒間に5~8回、ゆっくりなひとでも1~2回ほどキーを入力できることを鑑みると、キーアップされてからまあお茶でも飲んで1秒ぐらい待ってもらってから検索結果を表示しましょうと独断で待ち時間を設定した。


早速その方法

function test($searchbox){
  var me = this;
  this.$oj = $searchbox;//keyupイベントを設定するtextbox
  this.keyup_stack = [];//keyup時にpush、setTimeout時にpopを行うスタック

  this.bindShowCand = function(){
    me.$oj.keyup(function(){
      me.keyup_stack.push(1);
      setTimeout((function(me_){
        return function(){
          me_.keyup_stack.pop();
          //以下が重要、スタックが空になったらサーバーへ問い合わせ実行
          if(me_.keyup_stack.length == 0){
            me_.showCandidate();
          }
        };
      })(me),1000);//1秒間待ち
    });
  };
  this.showCandidate = function(){
    //ここにAjaxの処理を書いていく
    $.post(...);
  };

  me.bindShowCand();
}
var hoge = new test($(":text.searchbox"));

ポイントはスタックが空になったら実行するっていう処理。
keyupされるたびにスタックに1をpushしていきsetTimeoutで匿名関数が実行されるたびにpopしていく。まあこれならわざわざスタックにしなくてもスカラの数値のインクリメントとデクリメントを繰り返していっても同じことはできるが。
(ただし、最後かどうかを判定する入れ物が配列じゃないと、関数の引数に渡しても参照渡しにならないので、注意が必要かも)
オブジェクト指向でsetTimeoutから自身のメソッドやプロパティを呼び出す処理に苦戦したけどこれで希望通りの実装が出来た。