🎯 やりたかったことは?
業務画面で一覧テーブルを作っていると、
「セルを直接編集したい」
「行をドラッグして並び替えたい」
「列幅をユーザーが変えられるようにしたい」
「スクロール位置を取得したい」
といった機能が欲しくなることがあります。
普通のHTMLテーブルだけで全部作ろうとすると、
それぞれの処理を自前で実装する必要があります。
そこで実務で使ったのが Tabulator でした。
Tabulatorにはテーブル操作に必要な機能がかなり揃っていますが、
実際に使い始めると、
「セル編集後のイベントなんだっけ?」
「行移動が終わったタイミングを取りたい」
「描画完了後に処理したい」
とイベント名を確認することがよくありました。
今回はシリーズ第1回として、
Tabulatorを軽く紹介しつつ、table.on() でよく使うイベントをまとめます。
各項目には実際に触れる動作サンプルも付けています。
🚀 急いでいる人向け
Tabulatorでは、テーブル生成後に
table.on("イベント名", function (...) {
// 処理
});
の形でイベントを登録できます。
今回扱うイベントはこちらです。
| イベント | タイミング |
|---|---|
cellEdited |
セルの編集が完了した |
rowMoved |
行の移動が完了した |
renderComplete |
テーブルの描画が完了した |
columnResized |
ユーザーが列幅を変更した |
scrollVertical |
テーブルを縦スクロールした |
例えばセル編集後なら、
table.on("cellEdited", function (cell) {
console.log(
cell.getValue()
);
});
と書けます。
イベントごとに渡される引数が違うので、
「いつ呼ばれるか」と「何が取得できるか」を押さえておけば使いやすいです。
💡 この記事で分かること
- Tabulatorがどんなライブラリなのか
- Tabulator 5.5系の基本的な使い方
-
table.on()の基本 -
cellEditedの使い方 -
rowMovedの使い方 -
renderCompleteの使い方 -
columnResizedの使い方 -
scrollVerticalの使い方 - イベント処理を書くときに気を付けたいこと
今回使用するバージョン
この記事は、実務で使用した Tabulator 5.5系 を前提にしています。
動作サンプルでは 5.5.4 を使用しています。
TabulatorはバージョンによってAPIやイベントが変わることがあるため、
別バージョンを使っている場合は、そのバージョンのドキュメントも確認してください。
なお、記事内の動作サンプルは CodePen を使っています。
コードだけ見たい場合は本文、
実際の挙動を確認したい場合はそのままサンプルを操作できます。
Tabulatorとは?
Tabulatorは、JavaScriptで高機能なテーブルUIを作れるライブラリです。
例えば、
- ソート
- フィルタ
- セル編集
- 行追加・削除
- 行の並び替え
- 列幅変更
- ページング
- イベント処理
などをまとめて扱えます。
自分が使った画面では、
単純にデータを一覧表示するだけではなく、
ユーザーがセルを編集
↓
編集内容を取得
行をドラッグ
↓
新しい並び順を取得
列幅を変更
↓
変更後の幅を保持
といった操作が必要でした。
こういう処理を拾うときに使うのが、
今回の table.on() です。
まずは最小構成でテーブルを作る
HTML側にはTabulatorを表示する要素を用意します。
<div id="example-table"></div>
JavaScript側でデータを用意します。
const tableData = [
{
id: 1,
name: "Apple",
quantity: 10,
},
{
id: 2,
name: "Orange",
quantity: 20,
},
{
id: 3,
name: "Banana",
quantity: 15,
},
];
テーブルを生成します。
const table = new Tabulator(
"#example-table",
{
height: "250px",
layout: "fitColumns",
data: tableData,
movableRows: true,
columns: [
{
title: "ID",
field: "id",
width: 80,
},
{
title: "Name",
field: "name",
editor: "input",
},
{
title: "Quantity",
field: "quantity",
editor: "number",
},
],
}
);
これだけでも、
ID | Name | Quantity
1 | Apple | 10
2 | Orange | 20
3 | Banana | 15
のようなテーブルを作れます。
Name と Quantity は編集可能、
movableRows: true にしているので行の並び替えもできます。
table.on() の基本
イベントはTabulatorを生成したあとに登録します。
const table = new Tabulator(
"#example-table",
{
// 設定
}
);
table.on(
"イベント名",
function (...) {
// イベント発生時の処理
}
);
例えば行クリックなら、
table.on(
"rowClick",
function (event, row) {
console.log(
row.getData()
);
}
);
のような形です。
今回は、この中でも実務画面で使いどころが分かりやすいイベントを見ていきます。
cellEdited:セル編集後の処理
セルの編集が完了したタイミングで処理したい場合は、
cellEdited
を使います。
table.on(
"cellEdited",
function (cell) {
console.log(
"field:",
cell.getField()
);
console.log(
"value:",
cell.getValue()
);
}
);
例えば Name を編集すると、
field: name
value: Green Apple
のように取得できます。
動作サンプル
下のテーブルで Name または Quantity を編集すると、
編集した列名と値を表示します。
See the Pen Tabulator 5.5 Demo - cellEdited by RYMK Works (@RYMK-Works) on CodePen.
行データごと取得する
編集されたセルだけでなく、
その行全体のデータが欲しい場合は、
cell.getRow().getData()
を使えます。
table.on(
"cellEdited",
function (cell) {
const rowData =
cell
.getRow()
.getData();
console.log(rowData);
}
);
例えば、
{
id: 1,
name: "Green Apple",
quantity: 10,
}
のようなデータを取得できます。
実務では、
セル編集
↓
変更された行を取得
↓
変更フラグを立てる
といった処理にも使えます。
何の列が変わったか確認する
const field =
cell.getField();
でfield名を取得できます。
table.on(
"cellEdited",
function (cell) {
switch (cell.getField()) {
case "name":
console.log(
"Name変更"
);
break;
case "quantity":
console.log(
"Quantity変更"
);
break;
default:
break;
}
}
);
列によって処理を変えたい場合に使いやすいです。
rowMoved:行の移動が完了したとき
Tabulatorでは、
movableRows: true
を指定すると、
ユーザーが行をドラッグして並び替えできます。
const table = new Tabulator(
"#example-table",
{
movableRows: true,
// その他の設定
}
);
行の移動が完了したときは、
rowMoved
を使います。
table.on(
"rowMoved",
function (row) {
console.log(
row.getData()
);
}
);
動作サンプル
下のテーブルでは、
行をドラッグすると現在の並び順を表示します。
See the Pen rowMoved by RYMK Works (@RYMK-Works) on CodePen.
並び順を取得する
行移動後に、
現在の順番を取得して保存したいことがあります。
table.on(
"rowMoved",
function () {
const rows =
table.getRows();
const order =
rows.map(
(row, index) => ({
id:
row
.getData()
.id,
orderNo:
index + 1,
})
);
console.log(order);
}
);
例えば、
Banana
Apple
Orange
の順番にすると、
[
{
id: 3,
orderNo: 1,
},
{
id: 1,
orderNo: 2,
},
{
id: 2,
orderNo: 3,
},
]
のようなデータを作れます。
DB側に表示順を持っている場合は、
このタイミングで保存対象を作れます。
renderComplete:描画完了後に処理する
テーブルの描画が完了したあとに処理したい場合は、
renderComplete
を使います。
table.on(
"renderComplete",
function () {
console.log(
"render complete"
);
}
);
動作サンプル
下のサンプルでは、
ボタンからテーブルを再描画すると
renderComplete の発生回数を表示します。
See the Pen renderComplete by RYMK Works (@RYMK-Works) on CodePen.
初期表示だけだと思わない
renderComplete は、
テーブル生成時に1回だけ呼ばれる
というより、
テーブルの描画が完了したタイミング
で発生するイベントとして考えた方が分かりやすいです。
例えば、
データ変更
ソート
フィルタ
列変更
redraw
などでテーブルが再描画された場合にも関係してきます。
そのため、
table.on(
"renderComplete",
function () {
heavyFunction();
}
);
のように重い処理を無条件で入れてしまうと、
再描画が多い画面では負荷につながる可能性があります。
本当に描画のたびに必要な処理かは確認しておいた方がいいです。
columnResized:列幅変更後の処理
ユーザーが列幅を変更したあとに処理したい場合は、
columnResized
を使います。
table.on(
"columnResized",
function (column) {
console.log(
"field:",
column.getField()
);
console.log(
"width:",
column.getWidth()
);
}
);
例えば Name 列を広げると、
field: name
width: 240
のように取得できます。
動作サンプル
列の境界をドラッグすると、
変更した列名と現在の幅を表示します。
See the Pen columnResized by RYMK Works (@RYMK-Works) on CodePen.
列幅を保持する
画面を開き直しても、
ユーザーが変更した列幅を維持したい場合があります。
table.on(
"columnResized",
function (column) {
const columnInfo = {
field:
column.getField(),
width:
column.getWidth(),
};
console.log(
columnInfo
);
}
);
取得した値を、
localStorage
サーバー
DB
などへ保存する処理につなげられます。
scrollVertical:縦スクロール位置を取得する
テーブルが縦にスクロールされたときは、
scrollVertical
を使います。
table.on(
"scrollVertical",
function (top) {
console.log(
"scroll:",
top
);
}
);
top には現在の縦スクロール位置が渡されます。
scroll: 0
scroll: 120
scroll: 245
のような値になります。
動作サンプル
下のテーブルを縦にスクロールすると、
現在のスクロール位置を表示します。
See the Pen scrollVertical by RYMK Works (@RYMK-Works) on CodePen.
スクロールイベントでは重い処理をしない
scrollVertical は、
スクロール中に何度も発生します。
そのため、
table.on(
"scrollVertical",
function (top) {
// DBアクセス
// 大量DOM操作
// テーブル全体再描画
}
);
のような重い処理をそのまま書くと、
テーブルの動作が重くなる原因になります。
必要なら、
スクロール位置だけ保持
↓
処理は別タイミング
にしたり、
debounce や throttle を使ったりします。
今回のイベントを一覧で整理
今回扱ったものをまとめるとこうなります。
| イベント | 引数 | 使いどころ |
|---|---|---|
cellEdited |
cell |
セル編集後 |
rowMoved |
row |
行並び替え後 |
renderComplete |
なし | 描画完了後 |
columnResized |
column |
列幅変更後 |
scrollVertical |
top |
縦スクロール時 |
自分の場合、
編集
並び替え
表示状態の保持
描画後処理
あたりでイベントを使うことが多いです。
table.on() を使う理由
Tabulatorは内部でテーブルのDOMを管理しています。
特にVirtual DOMを使っている場合、
画面に必要な行を中心にDOMが生成・破棄されます。
そのため、
document
.querySelector(...)
.addEventListener(...);
のように、
Tabulator内部のDOMへ外側から直接イベントを登録すると、
対象DOMが作り直されたあとに期待通り動かなくなることがあります。
Tabulator側で用意されている、
table.on(...)
や、
formatter
editor
RowComponent
CellComponent
ColumnComponent
などを使った方が、
Tabulator側の処理に合わせて実装しやすくなります。
table.off() でイベントを解除する
登録したイベントは解除できます。
例えば、
function onCellEdited(cell) {
console.log(
cell.getValue()
);
}
を登録します。
table.on(
"cellEdited",
onCellEdited
);
解除するときは、
table.off(
"cellEdited",
onCellEdited
);
です。
同じ画面でTabulatorを作り直したり、
イベント登録処理を複数回呼んだりする構成では、
二重登録にも注意します。
⚠️ 実務で気を付けたいところ
イベント内でテーブル全体を再設定しすぎない
例えば、
table.on(
"cellEdited",
function () {
table.setData(
tableData
);
}
);
のように、
セル1個の変更だけでテーブル全体を再設定すると、
データ量が多い画面では処理が重くなることがあります。
変更対象が限定されているなら、
CellComponent
RowComponent
ColumnComponent
側のAPIで済まないか確認した方がいいです。
例えば行単位の更新で済むなら、
row.update({
quantity: 20,
});
のように対象だけ更新できます。
この辺りは次回、
行・列の追加、更新、削除と、テーブル全体の再描画を減らす方法
としてまとめる予定です。
イベントは「いつ発生するか」を確認する
似た名前でも、
編集中
編集キャンセル
編集完了
移動中
移動完了
描画開始
描画完了
でイベントは違います。
例えばセル編集なら、
cellEditing
cellEditCancelled
cellEdited
があります。
今回欲しいのが、
値が確定したあと
なら cellEdited です。
スクロール系イベントは特に軽くする
スクロールイベントは短時間に何度も発生します。
動作が重くなった場合は、
scrollVerticalの中で何をしているか
も確認ポイントになります。
📝 まとめ
Tabulatorでは、
table.on(...)
を使うことで、
テーブル上の操作をイベントとして取得できます。
今回扱ったのは、
| イベント | 用途 |
|---|---|
cellEdited |
セル編集後 |
rowMoved |
行移動後 |
renderComplete |
描画完了後 |
columnResized |
列幅変更後 |
scrollVertical |
縦スクロール |
です。
最初はイベント名だけ見ても、
どのタイミングで使えばいいか分かりにくい部分がありました。
実際に触ってみると、
操作
↓
イベント発生
↓
取得できる値を確認
までセットで見た方が分かりやすかったので、
今回は動作サンプルも一緒に載せました。
次回は、
行・列の追加、更新、削除と、テーブル全体の再描画を減らす方法
をまとめる予定です。