[HTML5] Drag and Drop API
未閱讀:Drag & Drop Elements with Vanilla JavaScript and HTML @ DigitalOcean(原 alligator.io,已遷移至 DigitalOcean)
HTML5 中的 Drag & Drop API 可以讓我們在瀏覽器中做到拖曳元素、排序元素、或者是讓使用者透過拖拉的方式把要上傳的檔案拉到瀏覽器當中。
在學習 HTML5 Drag & Drop API 時,最重要的是要去區分 Drag Source 和 Drop Target,因為它們會需要各自去監聽不同的事件。
假設我們要把下圖中藍色的圓從左邊的區域移到右邊的區域,那麼:

- Drag Source 指的是被點擊要拖曳的物件,也就是藍色的圓,通常是一個 element。
- Drop Target 指的是拖曳的物件被放置的區域,也就是右邊的綠色區域,通常是一個 div container。
前置動作
在開始實做前,有一些是需要知道和注意的前置動作。
事件(Event)
Drag & Drop 提供的間事件主要包含 dragstart, drag, dragend, dragenter, dragover, dragleave, drop,其中有些是針對 Drag Source 的,有些則是針對 Drop Target 的,整理如下表:
| Drag Source | Drop Target |
|---|---|
| dragstart | |
| drag | dragenter |
| dragover | |
| dragleave | |
| drop | |
| dragend |
- drag:在 drag source 被拖曳時會持續被觸發。
- dragover:當拖曳的 drag source 在 drop target 上方時會持續被觸發。
記得要針對
dragover取消預設行為(preventDefault),否則可能無法正確觸發 drop 事件。
CSS Style
針對要被拖曳的元素(Drag Source)可以透過 CSS 的屬性設定,避免讓使用者在拖曳該元素時選取到裡面的內容:
[draggable='true'] {
/*
To prevent user selecting inside the drag source
*/
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
HTML Attribute
針對能夠被拖曳的元素,在其 HTML 標籤上添加屬性 draggable="true":
<div id="drag-source" draggable="true"></div>
Drag and Drop Basic
示範如何將一個物件從一個 container (#source-container) 內拖曳到另一個 container (#target-container)內:
HTML
針對可以被拖曳的元素加上 draggable="true":
<div id="drag-drop-basic">
<div id="source-container">
<div id="drag-source" draggable="true"></div>
</div>
<div id="target-container"></div>
</div>
JavaScript
針對要被拖曳的元素(dragSource) 監聽 dragstart 事件,並且把要傳遞給 dropTarget 的資料透過 setData 加以設定:
let dragSource = document.querySelector('#drag-source');
dragSource.addEventListener('dragstart', dragStart);
function dragStart(e) {
e.dataTransfer.setData('text/plain', e.target.id);
}
針對要被置放的容器 dropTarget 監聽 drop 事件,來處理當使用者放掉的時候要執行的行為,並透過 getData 來取得傳遞的資料;監聽 dragenter 和 dragover 事件來避免預設行為:
let dropTarget = document.querySelector('#target-container');
dropTarget.addEventListener('drop', dropped);
dropTarget.addEventListener('dragenter', cancelDefault);
dropTarget.addEventListener('dragover', cancelDefault);
function dropped(e) {
console.log('dropped');
cancelDefault(e);
let id = e.dataTransfer.getData('text/plain');
e.target.appendChild(document.querySelector('#' + id));
}
function cancelDefault(e) {
e.preventDefault();
e.stopPropagation();
return false;
}
Demo & Source Code
Drag and Drop Basic @ CodePen
Drag and Drop with multiple sources in multiple containers
HTML
在能夠被拖放的容器上加上 data-role="drag-drop-container" 屬性:
<div id="drag-drop-basic">
<div id="source-container" data-role="drag-drop-container">
<div id="drag-source" draggable="true"></div>
</div>
<div id="target-container" data-role="drag-drop-container"></div>
</div>
JavaScript
允許多個可拖曳的物件:
// Allow multiple draggable items
let dragSources = document.querySelectorAll('[draggable="true"]');
dragSources.forEach((dragSource) => {
dragSource.addEventListener('dragstart', dragStart);
});
function dragStart(e) {
e.dataTransfer.setData('text/plain', e.target.id);
}
允許多個可置放的容器:
// Allow multiple dropped targets
let dropTargets = document.querySelectorAll('[data-role="drag-drop-container"]');
dropTargets.forEach((dropTarget) => {
dropTarget.addEventListener('drop', dropped);
dropTarget.addEventListener('dragenter', cancelDefault);
dropTarget.addEventListener('dragover', cancelDefault);
});
function dropped(e) {
cancelDefault(e);
let id = e.dataTransfer.getData('text/plain');
e.target.appendChild(document.querySelector('#' + id));
}
function cancelDefault(e) {
e.preventDefault();
e.stopPropagation();
return false;
}
Demo & Source Code
Drag and Drop in Multiple Container @ CodePen
修改拖曳時的 CSS 樣式
我們新增兩個 CSS 樣式分別套用在被拖曳的物件和被放置的容器上:
// For drag sources
.dragging {
opacity: 0.25;
}
// For drop target
.hover {
background-color: rgba(0, 191, 165, 0.04);
}
針對物件本身,我們在開始拖曳時添加樣式,結束拖曳時移除樣式:
function dragStart(e) {
this.classList.add('dragging');
e.dataTransfer.setData('text/plain', e.target.id);
}
function dragEnd(e) {
this.classList.remove('dragging');
}
針對容器,我們在進入容器時添加樣式,在離開或放置後移除樣式:
function dropped(e) {
let id = e.dataTransfer.getData('text/plain');
e.target.appendChild(document.querySelector('#' + id));
this.classList.remove('hover');
}
function dragOver(e) {
this.classList.add('hover');
}
function dragLeave(e) {
this.classList.remove('hover');
}
Demo & Source Code
Drag and Drop with CSS Style @ CodePen