LoginSignup
1
0

More than 3 years have passed since last update.

jQueryプラグインのDatatablesにボタンやイベントを追加しようとしたらドツボにはまった件について

Posted at

初投稿なのでアドバイスや指摘等あれば遠慮なくお願いします。

0.前置き

jQueryにはDataTablesという便利なテーブルプラグインがあり、それを使っていたのだが、問題にぶち当たり、いくらググっても解決しないので、自力で解決した。
もしかしたら誰も書いてないかもしれないので、メモついでに書いておく。
(C#にDataTableというものがあるらしいが、別物なので注意)

1.環境、目的

環境:jQuery 3.4.1, DataTables 1.10.18
目的:DataTablesを使った上で、表の中にボタンを置きたかった。

2.もくろみ

例えば売上テーブル(売上ID、売上日、顧客名)と売上明細テーブル(売上ID、商品ID、売上数量)があったとして、表には売上テーブルの情報を出力し、各行のボタンをクリックするとその明細が出る、みたいなことをやりたかった。
さらにセレクトボックスを置き、その選択内容で条件づけ(例えば売上日が1週間以内/1ヶ月以内/半年以内)し、ajaxを使ってページリロードなしで表を更新する、という風にしたかった。

3.問題

DataTablesのapiには表をクリアするメソッドや行を追加するメソッドがあるので、それを使ってデータ抽出のたびにクリア→1行ずつ追加、とすればできるだろうと考えた。
しかし実際に使ってみると、追加する行のデータは配列で渡す必要があり、その中にボタンオブジェクトを入れて渡しても、正しく出力されない。
ならばと行を追加したあと自分でjQueryでhtmlをいじくってボタンを追加してみると、今度は一部の行にしかボタンが表示されない。
これはイベントも同様で、ボタンではなくテキストを置き、セルをクリックしたらページ遷移、というふうにイベントを設定しうとしたが、これも一部のセルにしか適用されなかった。以下、うまくいかなかった例:

失敗例
<script>
$(function(){
    $('#sales_table').DataTable();          //#sales_tableにDataTablesを適用
    $('tbody td').click(function(){         //tbodyの中のtdタグ全てにクリックイベントを設定。クリックすると行と列の番号をダイアログで出す
        alert("行:" + ($(this).parent().index() +1) + ", 列:" + ($(this).index() + 1) + "");
    });
})
</script>
</head>
<body>
<table id="sales_table">
    <thead>
        <tr><td></td><td></td></tr>
    </thead>
    <tbody>
        <c:forEach var="v" items="${sales}" varStatus="st">     //salesは売上データのコレクション
            <tr><td>${st.count }</td><td>${v }</td></tr>
        </c:forEach>
    </tbody>
</table>
</body>

4.原因の分析

DataTablesを適用したテーブルのhtmlをいじるとき、どうやら初期表示される範囲に視野が限定されることがわかった。
DataTablesにはページング機能があり、デフォルトで10行ずつ表示するようになっている。この場合、データが10行を超えていると、最初の10行にしかボタンやイベントがつかないことになる。
色々試してみると、DataTablesでは内的に存在しているテーブルと、見えているテーブルの2つがあるらしいことがわかってきた。
内的なテーブルのうち、例えば10行ずつの表示ならば、最初の10行だけを切り取り、ブラウザに表示する。
apiを使わずにこちらの手が届くのは、見えているその10行のテーブルだけであり、それ以外の領域にアクセスしようとすると、DataTablesのapiを使わなければいけない。

DataTablesのapiには.draw()というメソッドがあり、apiで追加した行などはこれを実行しないと出力されない。
.draw()する前に、上記のようにイベントを設定したりしてみると、やはり追加された行には操作が適用されなかった。
これもどうやら、追加は内部的なテーブルに対してのみ行われ、.draw()を実行するとそれが表面にも反映され、こちらの手が届くようになる、という動作のように思える。

5.解決策

公式を見ても解決方法が見つからなかったので、力押しで解決することにした。
この問題はDataTablesが適用されている限りつきまとうので、ajaxで表を更新するたび、表を消して作り直す、という方針。
tableタグをdivで囲い、データを抽出するたびにその子要素のtableタグを消し、新しくtableタグを作ってデータを入れ直す。最後にDataTablesを適用。
これなら自分のやりたいことが実現できた。
以下の成功例では、ajaxによる更新を行っていないが、ajaxの.done()の中のコールバックで上記のことをやればよい。

成功例
<script>
$(function(){
    $('tbody td').click(function(){         //先にクリックイベントを付ける。今回はめんどくさいのでajaxによる更新はなし
        alert("行:" + ($(this).parent().index() +1) + ", 列:" + ($(this).index() + 1) + "");
    });
    $('#sales_table').DataTable();
})
</script>
</head>
<body>
<table id="sales_table">
    <thead>
        <tr><td></td><td></td></tr>
    </thead>
    <tbody>
        <c:forEach var="v" items="${sales}" varStatus="st">
            <tr><td>${st.count }</td><td>${v }</td></tr>
        </c:forEach>
    </tbody>
</table>
</body>

apiを使ってスマートにやろうと思うとこういうドツボにはまる、ということをすでに2,3回経験している。
野蛮人みたいに、自分のできることでゴリ押しするという選択肢を、もっと上位に持ってきたほうがいいのかもしれない。

6.まとめ

もっとスマートな方法があったら教えてください。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0