jQuery によるシンプルなテーブルフィルタリング処理の実装を考えてみる

ちょっと業務で必要になったので、jQuery によるテーブルフィルタリング処理の実装を考えてみます。
まずはなるべくシンプルで、フィルタ条件の入力フィールドとかもプラグインが勝手に生成するんじゃなくて、利用者側が準備する仕様にしたいと思います。

仕様

  • フィルタ条件の入力フィールドは、プラグイン利用者側が書く
  • どの条件入力フィールドがどの列に対応するかは、プラグイン実行時に利用者が指定する
  • 条件入力フィールドはラジオボタン、プルダウンメニューにも対応
  • 条件入力フィールドにキー入力、値変更が発生したら、自動的にフィルタリングが行われる
  • オプションでフィルタリングの自動実行が抑止でき、プラグイン利用者の任意のタイミングでフィルタリングを行うことができる

プラグインの実行方法

以下のような感じで実行できるようにしてみます。

フィルタ条件の入力フィールド

とりあえずテキストフィールド、ラジオボタン、プルダウンメニューに対応させます。

  1. <h2>Filter</h2>
  2. <dl class="filters">
  3. <dt>Class:</dt>
  4. <dd>
  5. <input name="class-filter" type="radio" value="" checked/>all
  6. <input name="class-filter" type="radio" value="core"/>core
  7. <input name="class-filter" type="radio" value="ui"/>ui
  8. </dd>
  9. <dt>Category:</dt>
  10. <dd>
  11. <input id="category-filter"/>
  12. </dd>
  13. <dt>Qty:</dt>
  14. <dd>
  15. <select id="qty-filter">
  16. <option>hoge</option>
  17. <option>fuga</option>
  18. </select>
  19. </dd>
  20. </dl>

データテーブル

ヘッダは thead、データは tbody に記述します。

  1. <h2>Data</h2>
  2. <table id="data">
  3. <thead>
  4. <tr>
  5. <th>No</th>
  6. <th>Class</th>
  7. <th>Category</th>
  8. <th>Qty</th>
  9. </tr>
  10. </thead>
  11. <tbody>
  12. <tr>
  13. <td>1</td>
  14. <td>core</td>
  15. <td>Ajax</td>
  16. <td>203</td>
  17. </tr>
  18. <tr>
  19. ……
  20. </tr>
  21. </tbody>
  22. </table>

JavaScript

列番号とフィルタ入力フィールドの対応を指定してプラグインを実行します。

  1. $('table').simpleTableFilter({
  2. filters : {
  3. 1 : 'input[name=class-filter]',
  4. 2 : '#category-filter',
  5. 3 : '#qty-filter'
  6. }
  7. });

プラグインの構造

たまには prototype ベースオブジェクトとか使わずだらだら書いてみます。

  1. $.fn.simpleTableFilter = function(option){
  2.  
  3. //オプション設定
  4. option = $.extend({
  5. autoFiltering : true
  6. },option);
  7.  
  8. return this.each(function(){
  9.  
  10. //table 要素の取得
  11. var target = $(this);
  12.  
  13. //--------------------------------------------------------------
  14. //フィルタリング処理の実装とテーブルへのバインド
  15. //--------------------------------------------------------------
  16.  
  17. //--------------------------------------------------------------
  18. //条件入力フィールドに変更があったらフィルタリング処理を起動する
  19. //--------------------------------------------------------------
  20. });
  21. }

「フィルタリング処理の実装とテーブルへのバインド」を行った後、「条件入力フィールドに変更があったらフィルタリング処理を起動する」実装をします。

フィルタリング処理の実装とテーブルへのバインド

「フィルタリング処理の実装とテーブルへのバインド」を行います。
これにより trigger() メソッドを使用することで、利用者が任意のタイミングでフィルタリング処理を起動できるようになります。

  1. $.fn.simpleTableFilter = function(option){
  2.  
  3. //オプション設定
  4. option = $.extend({
  5. autoFiltering : true
  6. },option);
  7.  
  8. return this.each(function(){
  9.  
  10. //table 要素の取得
  11. var target = $(this);
  12.  
  13. //--------------------------------------------------------------
  14. //フィルタリング処理の実装とテーブルへのバインド
  15. //--------------------------------------------------------------
  16. target.on('table-filtering',function(){
  17.  
  18. //tr でループ
  19. $(this).find('&gt; tbody &gt; tr').each(function(){
  20.  
  21. //一旦 tr を表示状態にする
  22. var tr = $(this).show();
  23.  
  24. //td でループ
  25. $(this).find('&gt; *').each(function(index){
  26.  
  27. //対応するフィルタを取得
  28. var filter = option.filters[index];
  29.  
  30. //フィルタの割り当てられてるか?
  31. if(filter){
  32.  
  33. //jQuery オブジェクト化
  34. filter = $(filter);
  35.  
  36. //td の値を小文字化して取得
  37. var data = $(this).text().toLowerCase();
  38.  
  39. //フィルタの値を小文字化して取得
  40. var filter_val = filter.val().toLowerCase();
  41.  
  42. //ラジオボタンの場合は選択された要素から値を取得
  43. if(filter.prop('type') == 'radio'){
  44. filter_val = filter.filter(':checked').val().toLowerCase();
  45. }
  46.  
  47. //フィルタの値が td の値に含まれてなかったら
  48. if(data.indexOf(filter_val) &lt; 0){
  49.  
  50. //tr を非表示にして
  51. tr.hide();
  52.  
  53. //td のループを抜ける
  54. return false;
  55. }
  56. }
  57. });
  58. });
  59. });
  60.  
  61. //--------------------------------------------------------------
  62. //条件入力フィールドに変更があったらフィルタリング処理を起動する
  63. //--------------------------------------------------------------
  64. });
  65. }

ロジックとしては、単純に tr 、td の入れ子でループし対応するフィルタ条件の値が td に含まれて無かったら、tr を非表示にするといったものです。

利用者が任意のタイミングでフィルタリング処理を起動したい場合、例えばフィルタリング実行ボタンをクリックした時にフィルタリングを行いたい場合は以下のように書きます。

  1. $('button#filtering').on('click',function(){
  2.  
  3. //フィルタリング処理の起動
  4. $('table').trigger('table-filtering');
  5.  
  6. });

条件入力フィールドに変更があったらフィルタリング処理を起動する

条件入力フィールドにキー入力や変更が発生する都度、自動的にフィルタリングが行われるようにします。

  1. $.fn.simpleTableFilter = function(option){
  2.  
  3. //オプション設定
  4. option = $.extend({
  5. autoFiltering : true
  6. },option);
  7.  
  8. return this.each(function(){
  9. //table 要素の取得
  10. var target = $(this);
  11.  
  12. //--------------------------------------------------------------
  13. //フィルタリング処理の実装とテーブルへのバインド
  14. //--------------------------------------------------------------
  15.  
  16. ……
  17.  
  18. //--------------------------------------------------------------
  19. //条件入力フィールドに変更があったらフィルタリング処理を起動する
  20. //--------------------------------------------------------------
  21.  
  22. //自動フィルタリングオプションが有効の場合のみバインドする
  23. if(option.autoFiltering){
  24.  
  25. //条件入力フィールドでループ
  26. for(var i in option.filters){
  27.  
  28. //キー入力後のフィルタリング処理を遅延実行させるためのタイマー変数
  29. var timer;
  30.  
  31. //フィルタ条件入力時
  32. $(option.filters[i]).on('keydown change',function(){
  33.  
  34. //直近のキー入力による実行待ちのフィルタリング処理をキャンセル
  35. if(timer) clearTimeout(timer);
  36.  
  37. //300ms 後のフィルタリング実行を予約
  38. timer = setTimeout(function(){
  39. target.trigger('table-filtering');
  40. },300);
  41. });
  42. }
  43.  
  44. //フィルタリングの実行
  45. target.trigger('table-filtering');
  46. }
  47. });
  48. }

setTimeout を使って 300ms 以内のキー操作は一回のアクションとみなして、フィルタリング処理を実行するようにしています。

フィルタリングの自動実行をしたくない場合は以下のように autoFiltering に false を指定してプラグインを実行します。

  1. $('table').simpleTableFilter({
  2. autoFiltering : false,
  3. filters : {
  4. 1 : 'input[name=class-filter]',
  5. 2 : '#category-filter',
  6. 3 : '#qty-filter'
  7. }
  8. });

(簡単ですが)とりあえず完成

必要最低限な機能しかないので、次は以下を考慮したバージョンも作ろうかと思います。

  • 条件入力フィールドにチェックボックスを指定可にする
  • 前方一致、後方一致、ワイルドカード一致等の一致条件を指定できるようにする
  • 入力フィールド以外もフィルタとして指定できるようにする(function、正規表現、真偽値)
  • フィルタリング開始終了時、フィルタリング中のコールバック関数を指定できるようにする
  • フィルタリング中の値参照等、各種 API を使用できるようにする

上記のソースに付け加えると確実に見通しが悪くなると思うので、次回は prototype ベースで作ります。