ドラッグ&ドロップ 実装解説|Drag and Drop UI JS & CSS Guide

ドラッグ&ドロップ デザイン見本

HTML の draggable 属性と JavaScript のドラッグイベントを組み合わせて、アイテムの順番をリアルタイムで並び替えるUIの実装デモです。コピーしてすぐに使えるHTML・CSS・JSのコードを掲載しています。

① draggable=”true” — ドラッグ可能な要素にする

ボタンで draggable 属性を切り替えられます

draggable: true

  • Task 1
  • Task 2
  • Task 3
  • Task 4
HTML
<ul class="task-list">
  <li class="task" draggable="true">Task 1</li>
  <li class="task" draggable="true">Task 2</li>
  <li class="task" draggable="true">Task 3</li>
  <li class="task" draggable="true">Task 4</li>
</ul>
CSS
.task-list {
  list-style: none;
  padding: 0;
  margin: 0 auto;
  max-width: 320px;
}

.task {
  padding: 10px 16px;
  margin: 6px 0;
  background: #f0f0f0;
  border: 1px solid #ccc;
  border-radius: 6px;
  cursor: move;
}
draggable=”true”

HTML要素に draggable="true" を設定すると、その要素をマウスでつかんでドラッグできるようになります。デフォルトは draggable="false" でドラッグ不可です。CSSで cursor: move を設定するとカーソルが十字矢印に変わり、ユーザーに「動かせる要素である」ことを伝えられます。

② .dragging クラス — ドラッグ中の視覚フィードバック

ボタンで .dragging クラスを適用・解除できます

Task 1 の状態: ドラッグ中

  • Task 1
  • Task 2
  • Task 3
  • Task 4
HTML
<div class="task-stage">
  <ul class="task-list">
    <li class="task" draggable="true">Task 1</li>
    <li class="task" draggable="true">Task 2</li>
    <li class="task" draggable="true">Task 3</li>
    <li class="task" draggable="true">Task 4</li>
  </ul>
</div>
CSS
.task-stage {
  background: #1a1a2e;
  padding: 24px 20px;
  border-radius: 12px;
}

.task-list {
  list-style: none;
  padding: 0;
  margin: 0 auto;
  max-width: 300px;
}

.task {
  padding: 10px 14px;
  margin: 6px 0;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 8px;
  color: rgba(255,255,255,0.8);
  cursor: move;
  transition: opacity 0.2s, background 0.2s;
  user-select: none;
}

.task.dragging {
  opacity: 0.35;
  background: rgba(148,163,184,0.18);
}
JS
(function() {
  var taskList = document.querySelector('.task-list');
  var tasks = taskList.querySelectorAll('.task');

  tasks.forEach(function(task) {
    task.addEventListener('dragstart', function() {
      task.className = 'task dragging';
    });

    task.addEventListener('dragend', function() {
      task.className = 'task';
    });
  });
})();
dragstart / dragend イベント

dragstart はドラッグ開始時、dragend はドラッグ終了時(成否問わず)に発火します。開始時に .dragging クラスを付与して半透明・色変化させ、終了時に外すことで「今どのアイテムを動かしているか」を視覚的に伝えられます。opacitybackgroundtransition を加えると切り替えが滑らかになります。

③ 並び替えの実装 — getBoundingClientRect() と insertBefore() でドロップ位置を判定

タスクをドラッグして順番を入れ替えてみてください

  • ☰ Task 1
  • ☰ Task 2
  • ☰ Task 3
  • ☰ Task 4
HTML
<ul class="task-list">
  <li class="task" draggable="true">Task 1</li>
  <li class="task" draggable="true">Task 2</li>
  <li class="task" draggable="true">Task 3</li>
  <li class="task" draggable="true">Task 4</li>
</ul>
CSS
.task-list {
  list-style: none;
  padding: 0;
  margin: 0 auto;
  max-width: 320px;
}

.task {
  padding: 10px 16px;
  margin: 6px 0;
  background: #f0f0f0;
  border: 1px solid #ccc;
  border-radius: 6px;
  cursor: move;
  transition: opacity 0.2s, background 0.2s;
  user-select: none;
}

.task.dragging {
  opacity: 0.4;
  background: #b0c4de;
}
JS
(function() {
  var taskList = document.querySelector('.task-list');
  var tasks = taskList.querySelectorAll('.task');
  var movingEl = null;

  tasks.forEach(function(task) {
    task.addEventListener('dragstart', function() {
      movingEl = task;
      task.className = 'task dragging';
    });

    task.addEventListener('dragend', function() {
      movingEl.className = 'task';
      movingEl = null;
    });

    task.addEventListener('dragover', function(e) {
      e.preventDefault();
      var targetEl = e.target;
      if (targetEl !== movingEl) {
        var rect = targetEl.getBoundingClientRect();
        var midY = rect.top + rect.height / 2;
        if (e.clientY > midY) {
          taskList.insertBefore(movingEl, targetEl.nextSibling);
        } else {
          taskList.insertBefore(movingEl, targetEl);
        }
      }
    });
  });
})();
getBoundingClientRect() でターゲットの中心を求め、上下で挿入位置を切り替える

dragover イベントはドラッグ中の要素が別の要素の上を通過するたびに発火します。getBoundingClientRect() でホバー先の位置・サイズを取得し、縦の中心(top + height / 2)を算出します。マウスのY座標(e.clientY)が中心より下なら要素の後ろへ、上なら前へ insertBefore() で挿入します。末尾に挿入する場合は targetEl.nextSiblingnull になるため、自動的にリストの最後へ追加されます。


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

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