【PR】を含みます。

フロントエンド

【JavaScript】ドラッグ&ドロップで要素を移動する方法とサンプルコード

JavaScript ドラッグ&ドロップで要素を移動する方法とサンプルコード

JavaScriptでドラッグ&ドロップ機能を実装する方法を2つのパターンで解説します。

エリア内での自由な要素移動と、リスト要素の並び替え機能の実装方法を、マウス・タッチ両対応のコードとともに紹介します。

また、実装に役立つ主要なライブラリについてもまとめています。

ドラッグ&ドロップでファイルアップロード機能を実装する方法は以下の記事で紹介しています。

あわせて読む
JavaScript ドラッグ&ドロップによるファイルアップロード機能を実装する方法と注意点
【JavaScript】ドラッグ&ドロップによるファイルアップロード機能を実装する方法と注意点

もくじ【サンプル】ドラッグ&ドロップによるファイルアップロード機能【実装コード】ドラッグ&ドロップによるファイルアップロード機能サーバーへのアップロード処理(擬似)よくあるエラーと対処法実装時の注意点 ...

【サンプル】ドラッグ&ドロップで特定のエリア内で要素を自由に動かす

Drag me

【実装コード】ドラッグ&ドロップで特定のエリア内で要素を自由に動かす

Copyをクリックするとコピーできます。

HTML
Copy
<div id="drag-area" class="drag-area">
  <div id="draggable-box" class="draggable-box">Drag me</div>
</div>
CSS
Copy
.drag-area {
  position: relative;
  width: 100%;
  height: 300px;
  border: 2px solid #ccc;
}
.draggable-box {
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  width: 100px;
  height: 100px;
  text-align: center;
  color: #fff;
  background: #f09896;
  cursor: grab;
  touch-action: none;
}
.draggable-box.dragging {
  cursor: grabbing;
}
JavaScript
Copy
const box = document.getElementById('draggable-box');
const area = document.getElementById('drag-area');
let offsetX = 0;
let offsetY = 0;
let isDragging = false;
const getEventPosition = (e) => {
  if (e.touches) {
    return {
      x: e.touches[0].clientX,
      y: e.touches[0].clientY
    };
  }
  return {
    x: e.clientX,
    y: e.clientY
  };
};
const startDrag = (e) => {
  e.preventDefault();
  const pos = getEventPosition(e);
  const rect = box.getBoundingClientRect();
  offsetX = pos.x - rect.left;
  offsetY = pos.y - rect.top;
  isDragging = true;
  box.classList.add('dragging');
};
const duringDrag = (e) => {
  if (!isDragging) return;
  const pos = getEventPosition(e);
  const areaRect = area.getBoundingClientRect();
  let left = pos.x - areaRect.left - offsetX;
  let top = pos.y - areaRect.top - offsetY;
  const maxLeft = area.clientWidth - box.offsetWidth;
  const maxTop = area.clientHeight - box.offsetHeight;
  left = Math.max(0, Math.min(left, maxLeft));
  top = Math.max(0, Math.min(top, maxTop));
  box.style.left = left + 'px';
  box.style.top = top + 'px';
};
const endDrag = () => {
  isDragging = false;
  box.classList.remove('dragging');
};
const adjustPosition = () => {
  const areaRect = area.getBoundingClientRect();
  const maxLeft = area.clientWidth - box.offsetWidth;
  const maxTop = area.clientHeight - box.offsetHeight;
  const currentLeft = parseFloat(box.style.left) || 0;
  const currentTop = parseFloat(box.style.top) || 0;
  const newLeft = Math.max(0, Math.min(currentLeft, maxLeft));
  const newTop = Math.max(0, Math.min(currentTop, maxTop));
  box.style.left = newLeft + 'px';
  box.style.top = newTop + 'px';
};
// === イベント登録 ===
// マウス
box.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', duringDrag);
document.addEventListener('mouseup', endDrag);
// タッチ
box.addEventListener('touchstart', startDrag, { passive: false });
document.addEventListener('touchmove', duringDrag, { passive: false });
document.addEventListener('touchend', endDrag);
window.addEventListener('resize', adjustPosition);

【サンプル】ドラッグ&ドロップでリスト要素を並び替える

  • アイテム1
  • アイテム2
  • アイテム3
  • アイテム4

【実装コード】ドラッグ&ドロップでリスト要素を並び替える

Copyをクリックするとコピーできます。

HTML
Copy
<ul id="sortable-list" class="sortable-list">
  <li class="sortable-item">アイテム1</li>
  <li class="sortable-item">アイテム2</li>
  <li class="sortable-item">アイテム3</li>
  <li class="sortable-item">アイテム4</li>
</ul>
CSS
Copy
.sortable-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.sortable-item {
  margin: 4px 0;
  padding: 12px;
  background: #fff6f6;
  border: 1px solid #ccc;
  transition: 0.3s ease;
  cursor: grab;
}
.dragging {
  opacity: 0.4;
}
JavaScript
Copy
const list = document.getElementById('sortable-list');
let draggingItem = null;
const getDragAfterElement = (container, y) => {
  const items = [...container.querySelectorAll('.sortable-item:not(.dragging)')];
  return items.reduce((closest, child) => {
    const box = child.getBoundingClientRect();
    const offset = y - (box.top + box.height / 2);
    if (offset < 0 && offset > closest.offset) {
      return { offset: offset, element: child };
    } else {
      return closest;
    }
  }, { offset: Number.NEGATIVE_INFINITY }).element;
};
// マウス
list.querySelectorAll('.sortable-item').forEach((item) => {
  item.setAttribute('draggable', 'true');
  item.addEventListener('dragstart', (e) => {
    draggingItem = item;
    item.classList.add('dragging');
  });
  item.addEventListener('dragend', () => {
    if (draggingItem) {
      draggingItem.classList.remove('dragging');
      draggingItem = null;
    }
  });
});
list.addEventListener('dragover', (e) => {
  e.preventDefault();
  const afterElement = getDragAfterElement(list, e.clientY);
  if (afterElement == null) {
    list.appendChild(draggingItem);
  } else {
    list.insertBefore(draggingItem, afterElement);
  }
});
// タッチ
let touchDraggingItem = null;
list.querySelectorAll('.sortable-item').forEach((item) => {
  item.addEventListener('touchstart', (e) => {
    touchDraggingItem = item;
    item.classList.add('dragging');
  });
  item.addEventListener('touchmove', (e) => {
    e.preventDefault(); // スクロール防止
    const touchY = e.touches[0].clientY;
    const afterElement = getDragAfterElement(list, touchY);
    if (afterElement == null) {
      list.appendChild(touchDraggingItem);
    } else {
      list.insertBefore(touchDraggingItem, afterElement);
    }
  });
  item.addEventListener('touchend', () => {
    if (touchDraggingItem) {
      touchDraggingItem.classList.remove('dragging');
      touchDraggingItem = null;
    }
  });
});

並び替え・自由移動ライブラリ比較表

ライブラリ名並び替え対応(リスト)自由移動対応(XY座標)スマホ対応特徴・備考
SortableJS可能不可対応軽量・簡単。
並び替え特化。
自由移動は非対応。
Interact.js不可可能対応境界制限・スナップ・リサイズも可能。
自由移動なら最有力。
GridStack.js可能グリッド状に動かせる対応ダッシュボード風のレイアウトに強い。

用途別おすすめ

やりたいこと名おすすめライブラリ
シンプルなリストの並び替えSortableJS
自由な位置に動かしたい(エリア内など)Interact.js
並び替え+自由移動(グリッド風UI)GridStack.js

まとめ

JavaScriptでドラッグ&ドロップ機能を実装する方法を2つのパターンに分けて紹介しました。

  • エリア内で自由に要素を移動:カスタムUIやゲーム要素の実装に最適
  • リストの並び替え:タスクリストや管理画面での項目の並び替えに活用可能
  • マウス・タッチ両対応:モバイルデバイスでも快適に操作可能

自前実装することで、柔軟なカスタマイズが可能です。

また、必要に応じてSortableJSやInteract.jsなどのライブラリを活用することで、より高度な機能を実装することもできます。

ぜひ、ご自身のプロジェクトに合わせてカスタマイズしてみてください。

ドラッグ&ドロップでファイルアップロード機能を実装する方法は以下の記事で紹介しています。

あわせて読む
JavaScript ドラッグ&ドロップによるファイルアップロード機能を実装する方法と注意点
【JavaScript】ドラッグ&ドロップによるファイルアップロード機能を実装する方法と注意点

もくじ【サンプル】ドラッグ&ドロップによるファイルアップロード機能【実装コード】ドラッグ&ドロップによるファイルアップロード機能サーバーへのアップロード処理(擬似)よくあるエラーと対処法実装時の注意点 ...

-フロントエンド
-