以前、Handsontableで添付ファイルを表示するカスタマイズを行いましたが、それに引き続きのカスタマイズです。
元々Handsontableを使用していた案件で「セルをマージして表示したい」という要望が出てきたので調べてみたのですが、どうやらライブラリ側でセルのマージ機能は提供されているようで。ただし、この提供されているセルのマージ機能が思っていたものと違うものでした。
そこで、提供されているセルのマージ機能ではなく、独自のカスタマイズで実装してみた、という形です。
結論としては、タイトルの通り、セルのマージ”みたい”な表示をする事で実現しました。
前述のライブラリ側で提供されているマージ機能について調べてみると、やっぱり「思ったものと違うよ」という意見もあるようで。代替案の一つとしてシェアします。
Handsontableの概要
Handsontableはブラウザ(=JavaScript)側でExcelのようにデータをグリッドに表示できるライブラリです。
公式ドキュメントがとっても見やすいので、詳しい機能などは公式を参照のこと。APIが非常に豊富なのでドキュメントのボリュームも凄いです。さくっとどんな事ができるのか確認したい場合はQiitaに良記事があったので、そちらも参考になるかもしれません。
(このページをご覧になっているという事は、この説明も要らないかもしれないですね。)
やりたいこと
例えば、以下はWikipediaから拝借した日本の市の面積トップ10の表ですが、9位、10位に注目。岩手県が続いている。
このように複数行続けて同じ値がある時は、以下のようにセルをマージして表示したい。
準備
環境
Handsontableを使用する為、下記のhtmlとJavaScriptを用意しました。
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css" /> </head> <body> <div id="container"></div> <script src="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js"></script> <script src="../javascript/handsontable.js"></script> </body> </html>
handsontable.js
(function() { var data = [ { prefecture: '岐阜県', city: "高山市", area: 2177.6, }, { prefecture: '静岡県', city: "浜松市", area: 1558.1, }, { prefecture: '栃木県', city: "日光市", area: 1449.8, }, { prefecture: '北海道', city: "北見市", area: 1427.4, }, { prefecture: '静岡県', city: "静岡市", area: 1411.9, }, { prefecture: '北海道', city: "釧路市", area: 1362.9, }, { prefecture: '山形県', city: "鶴岡市", area: 1311.5, }, { prefecture: '岩手県', city: "宮古市", area: 1259.2, }, { prefecture: '岩手県', city: "一関市", area: 1256.4, }, { prefecture: '広島県', city: "庄原市", area: 1246.5, }, ] const grid = document.getElementById("container"); const hot = new Handsontable(grid, { data: data, rowHeaders: true, colHeaders: true, columns: [ { data: "prefecture", type: 'text'}, { data: "city", type: 'text'}, { data: "area", type: 'text'}, ], columnSorting: true }); })();
とりあえず参照元のランキングが表示された
こんな感じで表示される。ほぼほぼExcel。
これをカスタマイズしていきます。
カスタマイズ内容
独自レンダラを実装
Handsontableはセルの描画処理に手を加える事ができる。
これはセルのオプションにrendererを追加する事で実現するが、詳しい説明はドキュメント参照のこと。
実際に手を加えた内容としては、columnsで各列を定義している部分を、
columns: [ { data: "prefecture", type: 'text'}, { data: "city", type: 'text'}, { data: "area", type: 'text'}, ],
こんな感じに変更。
セル描画時にmy.rendererというレンダラを実行する。
columns: [ { data: "prefecture", type: 'text', renderer: 'my.renderer'}, { data: "city", type: 'text'}, { data: "area", type: 'text'}, ],
ではmy.rendererはなにをやってるの、というとこんな感じ。
// register custom renderer Handsontable.renderers.registerRenderer('my.renderer', function(instance, td, row, col, prop, value, cellProperties) { const top = instance.getDataAtCell(row-1, 0); const current = instance.getDataAtCell(row, 0); const bottom = instance.getDataAtCell(row+1, 0); if (top === current) { td.style.color = "white"; td.style.borderTopStyle = "hidden"; } if (bottom === current) { td.style.borderBottomStyle = "hidden"; } td.innerText = (value === null ? "" : value); return td; });
レンダラの内容をざっくり解説
1行ずつ描画されていく時に、キーの値(今回は県名)を
- 上の行の値と比較し、同じ値の場合、文字色を白、セルの上枠を非表示とする。
- 同様に下の値と比較し、同じ値の場合、セルの下枠を非表示とする。
するとこんな感じになります
めでたくマージっぽく表示されました。
ソートするとどうなる?
Excelでは、セルをマージするとソートができません。
今回の実装ではソートできます。
マージっぽい表示もソート後の値に応じて処理されます。
おわりに
欲を言えばマージした後、値を中央に持ってきたかったが、それは無理そう。