jquery: Tabulator 出来てみた4(複数選択に反応して plotly.js、等)

「等」なので説明が長くなりそうでやだなぁ。

jquery: Tabulator 出来てみた4(複数選択に反応して plotly.js、等)

若干の前置き

Tabulator の公式説明ではグラフを書くのに Sparklines だけを説明している。もちろんこれはこれでいいんだけれども。

jquery プラグインとしての Sparkline よりも、Wikipedia で Sparkline を調べた方がええぞ。これが向いてる場合もあるし、向いてない場合もある。ワタシが今やりたいのにはじぇんじぇん向いてない。つまり「スパークラインぢゃないチャントしたグラフ」りたいのだ。

ちうわけでグラフには plotly.js を使う。

てわけで出来てみたヤツ


コードの説明

『何してるかは「フレームのソースの表示」を見てちょ』というだけではちとツラかろう量になっちゃってるんで、少しだけ説明。

まず、使ったデータの説明。どこのなんのデータか、って話ではなくて。そうじゃなくて、データ構造の説明、ね。抜粋:

nameは日本語だけど、面倒なのでエンコードしてる。
 1 var allmaldata = [
 2   {
 3     "name": "\u9b54\u6cd5\u4f7f\u3044\u306e\u5ac1", 
 4     "datetime": [
 5       "2017-10-10 23:25:00", 
 6       "2017-10-17 23:36:00", 
 7     ], 
 8     "score": [
 9       8.58, 
10       8.57
11     ],
12     "members": [
13       117741, 
14       134443, 
15     ], 
16     "latest": {
17       "datetime": "2017-10-17 23:36:00", 
18       "members": 134443, 
19       "score": 8.57
20     }
21   }, 
22 ];

“2017-10-10 23:25:00″ には score が 8.58、members が 117741、という意味で、latest はそれらの「最新」。つまり latest はいわゆる冗長データなのだが、ここに書かれている Tabulator の制約のためにどうしても必要:

 1 $("#mal-stats-table").tabulator({
 2   //...
 3   "columns": [
 4     {
 5       "field": "name", 
 6       "frozen": true, 
 7       "headerFilter": "input", 
 8       "title": "name"
 9     }, 
10     {
11       "align": "right", 
12       // Array にアクセスして last index なんてことが出来たらステキだったのだが
13       // 出来ないのである。
14       "field": "latest.score",
15       "title": "score"
16     }, 
17     {
18       "align": "right", 
19       "field": "latest.members", 
20       "title": "members"
21     }
22   ], 
23   //...
24   "layout": "fitColumns", 
25   "selectable": true, 
26   //...
27 });

行選択可能な振る舞いにする方法は Selectable Rows に書かれてる。選択に反応する方法は Selection Callbacks に書かれてる。

で、これまでよりちょっとだけ凝った Filtering Data を使いたかった。列に張り付く Header Filtering だけだと BETWEEN 条件すら出来ないわけでしょ。なので Custom Filter を書いてるわけだが、正直 Examples のコレはわかりやすいとは言えないよな(html が不完全に書かれてるからである)。要するにアタシの例ではこうしてる:

html (実際のコードより単純化してある)
1 <div>
2   <span>
3     <label>Score: </label>
4     <input id="filter-value-score-lb" type="text" placeholder="lower" size=3 maxlength=4/>
5     <input id="filter-value-score-ub" type="text" placeholder="upper" size=3 maxlength=4/>
6   </span>
7   <button id="filter-clear">Clear Filter</button>
8 </div>
9 <div id="mal-stats-table"></div>
javascript (実際のコードより単純化してある)
 1 // ...
 2 
 3 // my filter function
 4 function customFilter(data) {
 5   var score_lb = parseFloat($("#filter-value-score-lb").val()) || 0.0;
 6   var score_ub = parseFloat($("#filter-value-score-ub").val()) || 10.0;
 7   return (score_lb <= data.latest.score && data.latest.score < score_ub);
 8 }
 9 
10 // Trigger setFilter function with correct parameters
11 function updateFilter() {
12   $("#mal-stats-table").tabulator(
13     "setFilter",
14     customFilter,  // filter field, or your function
15     "",  // filter type
16     ""   // filter value
17     );
18 }
19 
20 // Update filters on value change
21 $("#filter-value-score-lb, #filter-value-score-ub").change(updateFilter);
22 $("#filter-value-score-lb").keyup(updateFilter);
23 $("#filter-value-score-ub").keyup(updateFilter);
24 
25 // Clear filters on "Clear Filters" button click
26 $("#filter-clear").click(function(){
27   $("#filter-value-score-lb").val("");
28   $("#filter-value-score-ub").val("");
29 
30   $("#mal-stats-table").tabulator("clearFilter");
31 });

感想とか愚痴とか

まず「選択」操作が、まぁ WEB アプリの宿命ではあるけれど、慣れるまでわかりにくいよな。修飾キーなしで選択が切り替わっちゃうし、「Shift を押しながら」ではなくて「Shift を押しつつドラッグ」なのもね、うわぁ、て感じ。

あとな、「deselectRow」だけでなく「selectRow」も出来るのよ、けどね、フィルター状態は全然関係なく、ほんとに全部選択しちゃう。これはちょっとなぁ…。(→追記参照)

フィルターについては、今回の例のように行フィルタとヘッダフィルタを共存出来るのが面白いっちゃぁ面白い。いわゆる AND として条件を追加したければ Add Filter to Exsiting Filters で説明されてるように addFilter すればいいのだな、と考えがちだけれど、ワタシがやったようなのも場合によっちゃぁ便利であろうと思うわ。

2018-02-22追記

「ほんとに全選択しちゃう」への解: jquery Tabulator: 「visible records」を「全選択」