[脱jQuery]ネイティブでDrag&Dropやtableソート

2019/06/17
テーブルソートは、jQuery(tablednd.js)で比較的簡単に実装できる。 tablednd はマウスイベントを駆使して実装している感じだが、ここではHTML5のDND(Drag & Dropイベント)を利用して実装してみる。

HTML5ドラッグ&ドロップの仕様確認

まずは仕様を確認 ・HTML Living Standard:Drag and dropネイティブ HTML5 ドラッグ&ドロップ

サンプル&ライブラリ

tableの並び替え&アップデートスクリプトのライブラリ(PwJS)を作成してみた。

Github

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

「draggable = true」でドラッグ可能&ソート属性を生成

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判別関数

DNDイベント処理

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 が並び替え後に順番を振り直す事を前提にしている
いちから始める Docker - docker-compose を使う - (2020年)
AWS ECR を使ってみる
Laravel7 でマルチ認証
Mac に AWS Client を設定する
Laravel 7 リリース
v-html でHTML表示する
Laravel で Vue コンポーネントを使う
Laravel で Nuxt.js を使ってみる(Docker環境)
いちから始める Docker -コンテナをビルド- (2020年)
いちから始める Docker -起動してみる- (2020年)
Mac で MySQL(8系)
composer で vendor がインストールできない
Eloquent の日付を Carbon で扱う
webpack 4 入門(npm編)
[Mac]容量を減らす
DIコンテナはじめ
freee SDKを Laravel で使ってみる
freee API を使ってみる
Segueを利用しない画面遷移
Xcode11.3 で XVim2 を利用する
Codable で JSONを読み込み
Webpack入門(yarn編)
MacからLaradock PostgreSQLで接続エラー
Dockerで不要なコンテナ・イメージを削除
Mac で Laradock の構築
yarn インストール&プロジェクト作成
Laravel 6.x 構築(Homestead編)
Composer インストール
nvm インストール
npm install が Mac でエラー
HTMLタグでカーソルが同時処理(ミラーリング)されてしまう
DI(依存性注入)
[Ubuntu]Let's Encryptで無料の証明書を利用する
[Apache]Apache2.4のアクセス制限が変更
[Ubuntu]rootのログインとsudo権限追加
タミヤ マイコンロボット工作セットをMacに接続してみた
pgAdimn4 でブラウザで開けなくなる
Java8 を HomebrewとjEnvで構築
Android Studio環境構築 2019
ロケールの再構築
vagrant グループに Apacheを追加
Linux2 Apache2 + PHP7.3 + PostgreSQL10
Anadondaの削除
[Mac]初期設定メモ(CentOS)
[Mac]PostgreSQL起動
FormRequest を利用したバリデーション
[Debian]Node.js安定板インストール
try-catch で Exception検知
Middlewareを利用したルートグループ化
namespaceを利用したルートグループ化