以前、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では、セルをマージするとソートができません。

今回の実装ではソートできます。
マージっぽい表示もソート後の値に応じて処理されます。

おわりに
欲を言えばマージした後、値を中央に持ってきたかったが、それは無理そう。