はじめに
これは、Jspreadsheet Advent Calendar 2024の8日目の記事となります。
「Handsontable 使い方メモ1(基本)」と比較しながら書いていきます。
Hello World
導入と設定
実装
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Jspreadsheet</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jspreadsheet-ce/dist/jspreadsheet.min.css" media="screen">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css" type="text/css" />
</head>
<body>
<div id="grid"></div>
<script src="https://cdn.jsdelivr.net/npm/jspreadsheet-ce/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.js"></script>
<script type="text/javascript">
// 以降の記載をここを埋める -->
</script>
</script>
</body>
</html>
var data = [
[ '佐藤', 28 ],
[ '鈴木', 19 ],
[ '田中', 25 ]
];
var grid = document.getElementById('grid');
new jspreadsheet(grid, {data: data});
実行結果
説明
- 以下の4つのファイルを読み込む
jspreadsheet-ce/dist/index.min.js
jsuites/dist/jsuites.min.js
jspreadsheet-ce/dist/jspreadsheet.min.css
jsuites/dist/jsuites.min.css
- グリッドを表示する場所として
<div>
タグを用意して、その Element オブジェクトを取得する -
jspreadsheet
をnew
してテーブルを作成する- コンストラクタ引数の第一引数に先ほど取得した Element オブジェクトを渡す
- 第二引数にオプションを渡す
-
data
オプションに、グリッドに表示するデータを渡す
-
Handsontableとの違い
- Jspreadsheet は Jsuites に依存しているため、読み込みが必要となります
- 行ヘッダーと列ヘッダーがデフォルト表示になっています
データソース
jspreadsheet では、配列やオブジェクトなど幾つかのデータを取り込むことができる。
配列
let data = [
[ '佐藤', 28 ],
[ '鈴木', 19 ],
[ '田中', 25 ]
];
let grid = document.getElementById('grid');
new jspreadsheet(grid, {data: data});
- 二次元配列を使用した最も基本的な方法
オブジェクト
let data = [
{ name: '佐藤', age: 25, a: 'A' },
{ name: '鈴木', age: 11, b: 'B' },
{ name: '田中', age: 21 }
];
let grid = document.getElementById('grid');
new jspreadsheet(grid, {data: data});
- オブジェクトの配列も渡すことができる
- 各プロパティから値が取得され、グリッドに表示される
- どのプロパティが使用されるかは、先頭のオブジェクトで決まる模様
ネストされたオブジェクト
let data = [
{ name: {first: '佐藤', last: '一郎'}, age: 25 },
{ name: {first: '鈴木', last: '二郎'}, age: 11 },
{ name: {first: '田中', last: '三郎'}, age: 21 }
];
let grid = document.getElementById('grid');
new jspreadsheet(grid, {
data: data,
columns: [ // ★列のマッピングを定義できる
{ name: 'age' },
{ name: 'name.last' },
{ name: 'name.first' }
]
})
-
column
オプションで、列ごとの細かい定義を宣言できる -
column
のdata
オプションで、その列に表示する値をオブジェクトのどのプロパティから取得するかを指定できる - ネストされたオブジェクトの場合は
name.first
のようにドット区切りでプロパティはCE版では指定できません
ネストされたオブジェクトは、Pro版では指定可能です。
https://jspreadsheet.com/docs/data
Handsontableとの違い
MIT(6.2.2)版でも、ネストされたオブジェクトの場合は name.first
のようにドット区切りでプロパティを指定できる。
任意のオブジェクトを使う
JspreadsheetにはdataSchema
属性はない。
CE版では非対応
Handsontable - 任意のオブジェクトを使う
空のグリッドを作る
let grid = document.getElementById('grid');
new jspreadsheet(grid, {
data: [{}],
columns: [
{ name: 'name' },
{ name: 'age' }
],
minSpareRows: 1
})
-
columns
で、列の順序を指定する -
data
に空のデータ配列を渡す -
minSpareRows
に最低限表示する行数を設定する
指定数の空のグリッドを作る
minDimensions
に指定した行列数分の空グリッドを設定できる
let grid = document.getElementById('grid');
new jspreadsheet(grid, {
minDimensions: [4,3]
})
Handsontableとの違い
JspreadsheetにはdataSchema
属性はないため、data: []
を data: [{}]
に変更
データとグリッドを切り離す
グリッドを再描画してもデータの変更が反映されないようにしたい場合は、コンストラクタ引数で渡す data
をディープコピーにする方法がある。
ディープコピーを作る手段の1つとして、 JSON に一旦エンコードして、 JavaScript オブジェクトにデコードし直す方法がある。
let data = [
[ '佐藤', 28 ],
[ '鈴木', 19 ],
[ '田中', 25 ]
];
let grid = document.getElementById('grid');
let table = new jspreadsheet(grid, {
// JSON 文字列にしてから JavaScript オブジェクトに戻す
data: JSON.parse(JSON.stringify(data))
});
data.push([ '山田', 22 ]);
table.updateTable();
Handsontableとの違い
-
table.render()
からtable.updateTable()
に変更 -
table.updateTable()
は描画のみで、table.setData(data)
としないとデータは更新されない
プラグイン
調査中
Handsontableとの違い
コメントはプラグインなしで標準機能として使用できる。
let grid = document.getElementById('grid');
let table = new jspreadsheet(grid, {
minDimensions: [5,5],
allowComments: true
});
table.setComments([1, 1], 'This is a comment');
Hook(イベントハンドリング)
イベントハンドラを追加する
let grid = document.getElementById('grid');
let table = new jspreadsheet(grid, {
minDimensions: [5,5]
});
table.options.onselection = function(el, startRow, startCol, endRow, endCol) {
console.log(`${startRow}, ${startCol}, ${endRow}, ${endCol}`);
};
-
options.(指定イベント)
でイベントハンドラを登録できる
Handsontableとの違い
-
hooks
が無いため、options
を使用する - 複数の jspreadsheet インスタンスによる全てのグリッドのイベントは監視できない、特定のグリッドのみ
-
afterSelection
イベントはないため、onselection
イベントに変更
イベントの種類
公式ドキュメント を参照
- onafterchanges(records):変更後
- onbeforechange(el, cell, x, y, value):変更前
- oneditionend(el, cell, x, y, value, save):編集終了時
- oneditionstart(el, cell, x, y):編集開始時
- onbeforesave(el, obj, data):保存前
- onsave(el, obj, data):保存時
- onchange(el, cell, x, y, value, oldValue):変更時
- onselection(el, borderLeft, borderTop, borderRight, borderBottom, origin):選択時
- onsort(el, column, order):ソート時
- onbeforeinsertcolumn(el, columnNumber, numOfColumns, insertBefore):カラム挿入前
- oninsertcolumn(el, columnNumber, numOfColumns, historyRecords, insertBefore):カラム挿入時
- onbeforedeletecolumn(el, columnNumber, numOfColumns):カラム削除前
- ondeletecolumn(el, columnNumber, numOfColumns, historyRecords):カラム削除時
- onmovecolumn(el, o, d):カラム移動時
- onresizecolumn(el, column, width, oldWidth):カラムサイズ変更時
- onbeforeinsertrow(el, rowNumber, numOfRows, insertBefore):行挿入前
- oninsertrow(el, rowNumber, numOfRows, rowRecords, insertBefore):行挿入時
- onbeforedeleterow(el, rowNumber, numOfRows):行削除前
- ondeleterow(el, rowNumber, numOfRows, rowRecords):行削除時
- onmoverow(el, o, d):行移動時
- onresizerow(el, row, height, oldHeight):行サイズ変更時
- onchangeheader(el, column, oldValue, newValue):ヘッダ変更時
- onchangemeta(el, o, k, v):メタ情報変更時
- onchangepage(el, pageNumber, oldPage):ページ変更時
- onchangestyle(el, o, k, v):スタイル変更時
- oncomments(el, comments, title, cell, cell[0], cell[1]):コメント変更時
- oncreateeditor(el, cell, x, y, editor):エディタ作成時
- onblur(el):フォーカスを失う時
- onfocus(el):フォーカスがあたる時
- onload(el, obj):ロード時
- onmerge(el, cellName, colspan, rowspan):マージ時
- oncopy(el, row, hashString):コピー時
- onbeforepaste(el, data, x, y):ペースト前
- onpaste(el, data):ペースト時
- onundo(el, historyRecord):アンドゥ時
- onredo(el, historyRecord):リドゥ時
イベントハンドラを削除する
let grid = document.getElementById('grid');
let table = new jspreadsheet(grid, {
minDimensions: [5,5]
});
table.options.onselection = function(el, startRow, startCol, endRow, endCol) {
console.log(`${startRow}, ${startCol}, ${endRow}, ${endCol}`);
};
document.getElementById('remove').addEventListener('click', function () {
table.options.onselection = null;
});
-
options.(指定イベント)
に null でイベントハンドラを削除できる
登録されているイベントハンドラを取得する
Handsontableのhooks.getBucket
メソッドと同じものは存在しないため、個別にメソッドを作成する必要がある。
一度だけ実行されるイベントハンドラを登録する
Handsontableのhooks.once
メソッドと同じものは存在しないため、個別にメソッドを作成する必要がある。
任意のイベントを発火させる
Handsontableのhooks.run
メソッドと同じものは存在しないため、個別にメソッドを作成する必要がある。
カスタムイベントを直接作成する機能は標準で提供されていないため、JavaScript の標準機能を利用してカスタムイベントを作成する。
let grid = document.getElementById('grid');
let table = new jspreadsheet(grid, {
minDimensions: [5,5]
});
// 独自のイベントハンドラを登録
table.el.addEventListener('onButtonClick', function(e) {
console.log('click!!');
});
document.getElementById('button').addEventListener('click', function () {
// イベントを発火
table.el.dispatchEvent(new CustomEvent('onButtonClick'));
});
イベントハンドラに値を渡す
detail プロパティに、渡したい任意の値を含めることで実現しています。
ここでは、foo と bar という値を含めています。
let grid = document.getElementById('grid');
let table = new jspreadsheet(grid, {
minDimensions: [5,5]
});
// 独自のイベントハンドラを登録
table.el.addEventListener('onButtonClick', function(e) {
console.log('click!!', e.detail);
});
document.getElementById('button').addEventListener('click', function () {
// イベントを発火
table.el.dispatchEvent(
new CustomEvent('onButtonClick',{ detail: { foo: 'Hello', bar: 'World' }})
);
});
click!! ▶︎ {foo: 'Hello', bar: 'World'}
キーコード
let grid = document.getElementById('grid');
let table = new jspreadsheet(grid, {
minDimensions: [5,5],
});
// キーダウンのイベントハンドラを登録
table.el.addEventListener('keydown', function(e) {
if (e.keyCode === KEY_CODES.SPACE) {
console.log('space!');
} else if (isKey(e.keyCode, 'ARROW_LEFT|ARROW_RIGHT')) {
console.log('left or right!!');
}
});
const KEY_CODES = {
ARROW_LEFT: 37,
ARROW_RIGHT: 39,
SPACE: 32
}
function isKey(keyCode, baseCode) {
const keys = baseCode.split('|');
let result = false;
keys.forEach((key) => {
if (keyCode === KEY_CODES[key]) {
result = true;
return false;
}
});
return result;
}
-
beforeKeyDown
イベントがないため、keydown
イベントを登録して使用する -
KEY_CODES
に、よく使いそうなキーコードを定義する -
isKey()
メソッドを使えば、|
区切りで複数のキーのいずれかが入力されたことをチェックできる
Handsontableとの違い
- key イベント関連は定義されていない
- Jspreadsheet には、Handsontable のような
KEY_CODES
が設定されていない
よって、Handsontable の定義を参照
https://github.com/handsontable/handsontable/blob/master/handsontable/src/helpers/unicode.js
Editor
調査中、これが分かれば Jsuites のような部品を作るようになりそうです。
最後に
思っている以上に Handsontable なら存在するメソッドが不足している気がします。
参考になりそうなコードも少ないため、解析しながら作成していく必要がありますね。