もくじ
JavaScriptでドラッグ&ドロップ機能を実装する方法を2つのパターンで解説します。
エリア内での自由な要素移動と、リスト要素の並び替え機能の実装方法を、マウス・タッチ両対応のコードとともに紹介します。
また、実装に役立つ主要なライブラリについてもまとめています。
ドラッグ&ドロップでファイルアップロード機能を実装する方法は以下の記事で紹介しています。
-

【JavaScript】ドラッグ&ドロップによるファイルアップロード機能を実装する方法と注意点
もくじ【サンプル】ドラッグ&ドロップによるファイルアップロード機能【実装コード】ドラッグ&ドロップによるファイルアップロード機能サーバーへのアップロード処理(擬似)よくあるエラーと対処法実装時の注意点 ...
【サンプル】ドラッグ&ドロップで特定のエリア内で要素を自由に動かす
【実装コード】ドラッグ&ドロップで特定のエリア内で要素を自由に動かす
Copyをクリックするとコピーできます。
<div id="drag-area" class="drag-area"> <div id="draggable-box" class="draggable-box">Drag me</div></div>.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;}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をクリックするとコピーできます。
<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>.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;}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】ドラッグ&ドロップによるファイルアップロード機能を実装する方法と注意点
もくじ【サンプル】ドラッグ&ドロップによるファイルアップロード機能【実装コード】ドラッグ&ドロップによるファイルアップロード機能サーバーへのアップロード処理(擬似)よくあるエラーと対処法実装時の注意点 ...

