HTML5 Drag and Drop API
在使用 Drag and Drop API 以前,要在網頁上實作拖曳一般會使用 mousedown
、 mousemove
、 mouseup
三種事件來完成,拖曳(drag)這個動作可以想成是在 mousedown
的情況下同時進行 mousemove
,而 mouseup
會拿來結束拖曳。
由於這三個事件並不是特別為了處理拖曳而生,這種手段雖然行得通,但在撰寫邏輯時必須很小心,否則 Bug 解起來會很痛苦。
因此研究了 HTML 5 特別為了處理拖曳而生 Drag and Drop API ,希望未來處理拖曳時可以好受點,以下開始重點整理。
拖曳事件與對象
HTML5 提供多種用來觸發的拖曳事件,在使用拖曳事件前得先了解拖曳事件所監聽的對象。
拖曳的概念分成「拖曳對象」(Drag Source)和「放置目標」(Drop Target)。
拖曳對象就是被拖曳的元素,放置目標就是拖曳對象最終會著陸的目標元素。
有些事件是以拖曳對象為目標、有些事件是以放置目標為目標。最常用的大致如下:
拖曳對象的事件(Drag Source Event)
dragstart
:拖曳對象最一開始被拖曳時觸發(點擊時還沒,按住不放也還沒,按住不放開始拖的瞬間觸發)drag
:在拖曳對象被拖動的期間持續觸發
放置目標的事件(Dop Target Event)
dragenter
:當拖曳對象首次進入放置目標的範圍時觸發dragover
:只要拖曳對象是在放置目標的範圍內,會持續觸發drop
:當拖曳對象在放置目標的範圍內被釋放時觸發
實作簡單的拖曳
基本樣板如下, target
會被當作放置目標, source
會被當作拖曳對象。
1 | <div class="target"> |
設置拖曳對象
做為拖曳對象的元素,必須將 draggable
屬性設為 true
。
1 | <div class="source" draggable="true"></div> |
設置監聽事件
對拖曳對象監聽 dragstart
事件,對放置目標監聽 dragenter
、 dragover
、 drop
三個事件。
1 | // 連結 DOM |
設置回呼函式
當拖曳對象剛被拖動時,就把拖曳對象的 id
放進要傳送的資料內。
1 | function dragStart(e) { |
當拖曳對象放置目標上被釋放時,就把 id
從資料中取出來,然後利用 id
把 DOM 抓過來。
1 | function drop(e) { |
元素預設行為是不能被放置拖曳物的,因此在拖曳對象出現在放置目標上時,取消預設行為,讓放置目標可以被放置。
1 | function cancelDefault(e) { |
完成上述,就可以在兩個目標間來回拖曳東西。
See the Pen Simple Drag and Drop by Arel (@godlike0108) on CodePen.
細節補充
dataTransfer
上述實作時用到的 dataTransfer
物件,主要用來執行「拖曳對象」和「放置目標」間的資料傳遞。
dataTransfer
提供了 setData
和 getData
兩種方法傳遞資料, setData
負責決定要從拖曳對象發送的資料,因此多寫在 dragstart
的回呼函式內。
setData
第一個參數為資料類型,支援常用的 MIME ,第二個參數為資料內容,同樣類型的資料只能指定一份內容,指定第二份會蓋過第一份。
getData
負責接收資料,因此多與對應放置目標的事件 drop
一起使用。 getData
的唯一參數式資料類型,必須跟 setData
類型相同才收得到對應的資料。
使用 setData
、 getData
的良好習慣是,越特殊的資料類型優先寫,像 text/plain
這種支援度最高的類型則寫在最後面,如此才能確保資料可以被傳送。
拖曳效果(Drag Effects)
拖曳效果定義了常見的拖曳操作,有 copy
、 move
、 link
三種,有了被定義的拖曳效果,瀏覽器也會根據不同的拖曳操作顯示不同的提示。
dataTransfer
的 effectAllowed
和 dropEffect
屬性可以用來操作拖曳效果。只有當該放置目標的 dropEffect
與 拖曳對象的 effectAllowed
匹配時,才允許 drop
。
通常我們會在拖曳對象的 dragstart
時使用 dataTransfer.effectAllowed
限制該拖曳對象只有在哪種效果下才會拖曳成功。
然後在放置目標的 dragover
使用 dataTransfer.dropEffect
指定該放置目標的放置效果。
註: dropEffect
在 Chrome 瀏覽器有個 bug ,就是它只會顯示 none
,但並不影響實作,只是用 console 看可能會誤判。
Drag and Drop 清單
附上用 Drag and Drop API 實作的拖曳清單。
See the Pen Drag and Drop List by Arel (@godlike0108) on CodePen.
結論
比起使用舊的 mouse events 來說簡便很多,但由於是直接操作 DOM ,在和資料驅動的框架一起使用時(例如 Vue),邏輯上比較不直覺。