【JS】ドラッグ&ドロップで表示を入れ替えるUI

本記事では、ドラッグ&ドロップで複数のアイテムの順番を入れ替える実装方法を解説します。

実装例)To Do リストの順番を入れ替える

  • Task 1
  • Task 2
  • Task 3
  • Task 4
目次

HTML / CSS / JavaScript コード

<div class="container">
    <ul id="todo-list">
        <li draggable="true" class="draggable">Task 1</li>
        <li draggable="true" class="draggable">Task 2</li>
        <li draggable="true" class="draggable">Task 3</li>
        <li draggable="true" class="draggable">Task 4</li>
    </ul>
</div>

実装のポイント

ドラッグ&ドロップの基礎については以下記事でも解説していますが、改めて本記事でもポイントを解説します。

HTML構造:特定の要素をドラッグできるようにする

draggable="true"属性を設定することで、設定された要素がドラッグできるようになります。
今回の実装例ではタスク全てを個別にドラッグさせたいので、タスクの構成要素である<li>タグすべてにdraggable="true"を設定しています。

CSSスタイル:タスクの入れ替えができそうなスタイルを適用する

cursor: move;を設定することで、カーソルが十字の矢印に変化します。

JavaScriptイベント:ドラッグ&ドロップに関する各種イベントを設定する。

const listItems = document.querySelectorAll('#todo-list li');

listItems.forEach(item => {
  // ---略---
});

ドラッグ&ドロップを設定したい全タスクに対し、以下のイベントを設定します。

dragstartイベント

ドラッグが開始されたときに発生します。

item.addEventListener('dragstart', (e) => {
    draggingItem = item;
    item.classList.add('dragging');
});

ドラッグしているタスクをdraggingItemに代入し、ドラッグ中のアイテムにはdraggingクラス(CSSプロパティでグレーアウトさせる)を追加します。

dragendイベント

ドラッグ操作が終了したときに発生します。

item.addEventListener('dragend', () => {
    draggingItem.classList.remove('dragging');
    draggingItem = null;
});

dragstartイベントとは逆の操作を行います。
ドラッグ中のアイテムからdraggingクラスを削除し、draggingItem変数の中身をnullに戻します。

dragoverイベント

ドラッグ中のタスクが他のタスク上にあるときに発生します。

item.addEventListener('dragover', (e) => {
    e.preventDefault();
    const targetItem = e.target;
    if (targetItem !== draggingItem) {
        const bounding = targetItem.getBoundingClientRect();
        const offset = bounding.y + bounding.height / 2;
        const list = document.getElementById('todo-list');
        if (e.clientY - offset > 0) {
            list.insertBefore(draggingItem, targetItem.nextSibling);
        } else {
            list.insertBefore(draggingItem, targetItem);
        }
    }
});
  • まずe.preventDefault();でデフォルトの動作をキャンセルします。(これを行わないとタスクがドロップできません。)
  • const targetItem = e.target;でホバーされているタスクの情報を取得し、tagetItem変数に代入します。
  • if (targetItem !== draggingItem) {…}:ドラッグ中のタスクが他のタスク上にあるとき(自身のタスクでないとき)にのみ、以下の操作を行います。
    • const bounding = targetItem.getBoundingClientRect();:ホバーされているタスクの位置とサイズの情報を取得します。
    • const offset = bounding.y + bounding.height / 2;:ホバーされているタスクの縦方向の中心位置を計算します。
    • if (e.clientY - offset > 0) {...}:ドラッグ中のタスクの縦方向の位置がホバーされているタスクの中心よりも下にある場合、以下の操作を行います。
      • list.insertBefore(draggingItem, targetItem.nextSibling);:ドラッグ中のタスクをホバーされているタスクの次(下)に挿入します。
    • else {...}:それ以外の場合は、以下の操作を行います。
      • list.insertBefore(draggingItem, targetItem);:ドラッグ中のタスクをホバーされているタスクの前に挿入します。

【応用】タスクを追加して保存もできるToDoリスト

タスクを自由に追加でき、さらにブラウザを閉じても内容が保存されるToDoリストを作成しました。

実装コードも公開しているので、よろしければ参考にしてみてください。


当サイトで公開しているWebデザインやUIの実装例は、一覧として以下記事に纏めています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次