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