拖放功能,即將一個元素從一個區域,通過拖拽,放置到另一個區域。常見的應用是將文件或圖片從一個區域,拖放到另一個區域。中文常常把這表述成拖拽,實際上拖拽的描述並不准確,應該叫拖放,因為drag事件和drop事件是成對使用的,即拖拽和放置。 drag在拖拽動作發生時觸發,攜帶被拖拽元素的信息,drop在 ...
拖放功能,即將一個元素從一個區域,通過拖拽,放置到另一個區域。常見的應用是將文件或圖片從一個區域,拖放到另一個區域。中文常常把這表述成拖拽,實際上拖拽的描述並不准確,應該叫拖放,因為drag事件和drop事件是成對使用的,即拖拽和放置。
drag在拖拽動作發生時觸發,攜帶被拖拽元素的信息,drop在放置元素時觸發,接收傳遞的拖拽元素的信息。
由於常常表述成拖拽,所以有些人在實現拖動功能時以為會觸發drag事件,比如側邊欄拖拽。實際上drag是為拖放功能設計的(要配合drop),拖動(or拖拽)的功能應該用mousemove事件去實現,用錯事件就會覺得怎麼拖拽功能好難啊。
P.S.
drag和mousemove事件都是在移動滑鼠的過程中觸發,所以兩個事件是會衝突的,如果發現其中一個事件不生效,可以檢查下是不是因為有元素綁定了其中一個事件,導致另一個事件沒有trigger。
具體實現
拖放,拖拽和放置,那麼自然需要一個拖拽的區域,和一個放置的區域。
首先,要定義允許拖拽的元素。瀏覽器的預設行為是,文本、圖像和鏈接是允許拖拽的。即<p>、<img>、<a>標簽是預設允許拖拽的,其他元素要允許拖拽,則要設置draggle="true"。這個屬性不允許簡寫。<div draggable ></div>並不會生效。
其次,要定義拖拽的數據。比如,拖拽的是文本,則設置成文本格式,並設置拖拽的數據內容。拖拽的是圖片,則設置成圖片格式,設置數據內容。定義成圖片格式,那麼拖拽的時候,滑鼠旁邊就會顯示一張設置的圖片。設置的拖拽數據可以有多個。
先給拖拽的元素綁定dragstart事件,再設置dataTransfer。示例代碼如下:
function dragstart_handler(ev) {
// 添加拖拽數據
ev.dataTransfer.setData("text/plain", ev.target.innerText);
ev.dataTransfer.setData("text/html", ev.target.outerHTML);
ev.dataTransfer.setData(
"text/uri-list",
ev.target.ownerDocument.location.href,
);
}
拖放相關的事件對象event中,有一個dataTranster屬性,這個屬性保存著拖放過程中的數據。並有一些屬性和方法設置和操作這些數據。
比較常用到的屬性和方法有:
dropEffect
:設置放置操作的類型,可以修改放置時滑鼠的顯示。比如設置成none,滑鼠就會顯示成禁止的樣式。
effectAllowed
:設置拖放過程的操作類型,同樣影響滑鼠的顯示。
setData()
:設置拖放的數據。一般會在dragstart事件中用到。一個事件中,setData可以設置多個數據。但同類型的數據只能添加一項,重覆添加會被最後添加的覆蓋。
getData()
:檢索獲取拖放的數據。一般會在drop事件中用到。
具體可以參考MDN文檔。
最後,就是在drop區域中放置拖拽的元素。
一個代碼示例:
function drop_handler(ev) {
const data = ev.dataTransfer.getData("text/plain");
ev.target.textContent = data;
}
這樣,拖放就結束了。拖放功能也完成了。
在拖放過程中,有一些全局事件觸發,可以參考下表,具體請查看MDN文檔。
事件 | 觸發時刻 |
---|---|
drag |
當拖拽元素或選中的文本時觸發。 |
dragend |
當拖拽操作結束時觸發 (比如鬆開滑鼠按鍵或敲“Esc”鍵). |
dragenter |
當拖拽元素或選中的文本剛進入到一個可釋放目標時觸發。 |
dragleave |
當拖拽元素或選中的文本離開一個可釋放目標時觸發。 |
dragover |
當元素或選中的文本被拖到一個可釋放目標上時觸發(每 100 毫秒觸發一次)。 |
dragstart |
當用戶開始拖拽一個元素或選中的文本時觸發。 |
drop |
當元素或選中的文本在可釋放目標上被釋放時觸發。 |
一些問題
但是,這樣實現拖放功能會有一些體驗問題,滑鼠的樣式顯示可能不正確。
以下是一些可能的問題:
1.禁止放置的區域沒有顯示禁止圖標。
在區域上綁定dragover事件,設置dataTransfer.dropEffect='none'
並禁止預設行為e.preventDefault()
。
2.禁止放置的區域可以放置。
通常預設可以放置的區域是一些輸入標簽,比如<input>、<textarea>。在drop事件中,禁止預設行為e.preventDefault()
可以禁止放置。
3.拖拽過程和可放置區域中,滑鼠顯示禁止圖標。
在經過或者放置的區域上,在dragover和dragenter事件中禁止預設行為e.preventDefault()
,設置dataTransfer
為none
以外的值。
4.拖拽圖片時,滑鼠旁邊沒有出現圖片。
要在dragstart事件中,設置dataTransfer為圖片類型才會顯示一張圖片。如果拖拽的不是圖片,但是希望拖拽時有拖拽元素的圖片效果顯示,也可以設置dataTransfer為圖片,設置要顯示的圖片效果,然後再設置其他的數據。dataTransfer.setData()方法是可以設置多個類型的數據的。
這樣拖放功能的實現基本就完善了。最後在開始拖拽和放置的時候,可能會給拖拽元素和放置區域設置一些高亮的css效果,整個拖放功能的體驗就會很流暢。
參考:
draggable屬性:draggable - HTML(超文本標記語言) | MDN (mozilla.org)
拖放API:HTML 拖放 API - Web API | MDN (mozilla.org)
DataTransfer:DataTransfer - Web API | MDN (mozilla.org)