テーブルソートは、jQuery(tablednd.js)で比較的簡単に実装できる。 tablednd はマウスイベントを駆使して実装している感じだが、ここではHTML5のDND(Drag & Dropイベント)を利用して実装してみる。
まずは仕様を確認 ・HTML Living Standard:Drag and drop ・ネイティブ HTML5 ドラッグ&ドロップ
tableの並び替え&アップデートスクリプトのライブラリ(PwJS)を作成してみた。
PwJSライブラリ *PwTableDND.js, PwNode.jsを利用 html_samples/sotrable_table.html
色々と制御する必要があるので、ここでは主要部分だけ記述 *並び替えの保持やAPI更新は記述なし
「dragstart」「dragover」「drop」などのDNDイベントを登録して、Element取得・操作を行う
//各イベントハンドラ
function handleDragStart(event) {
//ここにドラッグアイテムの取得処理
event.target.style.opacity = '0.4'; //透明度を有効
event.stopPropagation();
}
function handleDrag(event) {
}
function handleDragEnter(event) {
}
function handleDragOver(event) {
event.dataTransfer.dropEffect = 'move'; //仕様でサポートしている dropEffect
event.preventDefault(); //伝播を止める
return false;
}
function handleDragLeave(event) {
}
function handleDrop(event) {
//ここにターゲットアイテムの取得処理
event.preventDefault(); //伝播を止める
event.stopPropagation(); //リンクキャンセル
}
function handleDragEnd(event) {
event.target.style.opacity = '' //透明度を無効
}
//各イベントの登録
document.addEventListener('dragstart', handleDragStart, false);
document.addEventListener('drag', handleDrag, false);
document.addEventListener('dragenter', handleDragEnter, false)
document.addEventListener('dragover', handleDragOver, false);
document.addEventListener('dragleave', handleDragLeave, false);
document.addEventListener('drop', handleDrop, false);
document.addEventListener('dragend', handleDragEnd, false);
伝播しないように preventDefault(), ドラッグでリンク誤動作しないように stopPropagation() を実行しているが、実際に実行してみるとよくわかる。
並び順のデータID「row-id」属性を持った「tr」で構成 「tbody」内の「tr」を入れ替えるようなスクリプトを作成する
City
Tokyo
Osaka
Nagoya
Kobe
querySelectorAll() などでElement や HTMLCollection を取得して、各行の属性を動的に設定 HTML5では「draggable = true」でドラッグ可能になる
....
var _this = this;
this.table_id = 'sortable-table';
this.row_id_column = 'row-id';
this.body_selector = '';
this.tr_selector = '';
this.sortable_tr_selector = '';
....
this.enableDrag = function() {
_this.loadSelectorColumn();
if (!this.table_id) return;
var row_id;
[].forEach.call(this.getElements(), function(element, index) {
_this.before_rows.push(element);
row_node = PwNode.byElement(element);
if (row_id = row_node.attr(_this.row_id_column)) {
row_node.setAttr('id', pw_row_id_column + row_id);
row_node.setAttr('order', index + 1);
row_node.setAttr('draggable', true);
if (!pw_app.isIE()) {
row_node.setAttr('ondragstart', "event.dataTransfer.setData('text/plain', null)");
}
}
});
}
this.loadSelectorColumn = function() {
_this.body_selector = '#' + _this.table_id + ' tbody';
_this.tr_selector = '#' + _this.table_id + ' tr';
_this.sortable_tr_selector = _this.body_selector + ' tr';
}
this.getElements = function() {
let elements = document.querySelectorAll(_this.sortable_tr_selector);
return elements;
}
FirefoxではHTMLタグに「ondragstart="event.dataTransfer.setData('text/plain', null)"」を指定しておかないと、何故か動作しなかった。 *pw_app.isIE() はライブラリのIE判別関数
dropイベントで、Drag Element, Drop Elementの取得・入れ替えを行う
function handleDragStart(event) {
let tr = event.target.closest('tr');
if (tr) _this.drag_item = event.target.closest('tr');
event.target.style.opacity = '0.4';
event.stopPropagation();
}
function handleDrop(event) {
var tr = event.target.closest('tr');
if (tr && _this.drag_item != tr) {
_this.target_item = tr;
}
event.target.style.opacity = '1.0'
let row_id = _this.drag_item.getAttribute(_this.row_id_column);
if (row_id && _this.target_item && _this.drag_item != _this.target_item) {
var tbody = PwNode.byQuery(_this.body_selector).first();
let drag_order = _this.drag_item.getAttribute('order');
let target_order = _this.target_item.getAttribute('order');
if (drag_order > target_order) {
tbody.insertBefore(_this.drag_item, _this.target_item);
} else if (drag_order < target_order) {
tbody.insertBefore(_this.drag_item, _this.target_item.nextElementSibling);
}
//テーブル並び替え後の処理を記述
//並び順管理、tr.order を振り直す etc..
}
event.preventDefault();
event.stopPropagation();
}
event.target.closest() で「tr」を取得 「tbody」内で insertBefore() を利用し、並び順を考慮しつつ Element を書き換える 色々な方法があると思うが、上記は「tr」の order が並び替え後に順番を振り直す事を前提にしている