了解如何使用 Drag and Drop API 為您的使用者創建交互式體驗

使用 Drag and Drop API,您可以定義頁面中的哪些元素是可拖動的,並在用戶拖動元素時攔截它。

它在現代瀏覽器中得到了很好的支持:

瀏覽器支持

在我們開始研究這個 API 之前,我們必須知道如何定義頁面中的可拖動元素。我們可以通過在頁面的 HTML 中添加 draggable 屬性並將其設置為 true 來實現:

<div draggable="true">
 ...
</div>

這就足以使元素成為可拖動的。

提示:圖片、文字選擇和鏈接默認情況下是可拖動的,除非您在它們上面設置了 draggable 為 false。

我們還可以從用戶計算機中將文件拖動到瀏覽器中。在這種情況下,我們傳輸的是文件

我們還需要澄清的另一點是元素可以拖放到哪裡。就像我們不能隨意拖動任何元素一樣,我們也不能隨意拖放到任何元素中。元素必須是有效的放置目標

要使元素成為放置目標,您需要侦聽其 dragover 事件,並根據需要從該事件返回 false 或調用 preventDefault()

const element = document.querySelector('#my-drop-target')
element.addEventListener('dragover', event => {
 event.preventDefault()
})

一旦完成了上述步驟,我們就擁有了一個可拖動元素和一个放置目標,就可以開始進一步操作了。我們可以與可拖動元素交互的事件有:

  • dragstart
  • drag
  • dragend

放置目標上可以交互的事件有:

  • dragenter
  • dragover
  • dragleave
  • drop

拖放操作概述及觸發的事件

當用戶開始拖動一個可拖動元素時,可以進行鼠標點擊並移動鼠標,或者也可以單擊並保持選擇,然後移動選擇區,此時會在元素上觸發 dragstart 事件:

element.addEventListener('dragstart', event => {
 //...
})

傳遞給事件處理函數的 event 對象是一個 DragEvent 對象。它是從更一般的事件對象中擴展出來的,與所有其他事件(鼠標,鍵盤,滾動等)共享。

此時,元素正在被拖動,並且正在觸發 drag 事件。當元素被拖動時,該事件會被觸發多次,因此我們必須像 scrollmouseover 事件一樣使用延遲。

一旦進入一個放置目標

  1. 會在放置目標上觸發 dragenter 事件
  2. 會在放置目標上觸發 dragover 事件

如果一個被拖動的元素首先進入一個放置目標,然後又移出該目標,則會在放置目標上觸發 dragleave 事件。

如果用戶釋放了滑鼠按鈕,則會在正在拖動的元素上觸發 dragend 事件,並且在放置目標上觸發 drop 事件。

拖曳數據:DataTransfer

與拖放相關的每個事件都是一個DragEvent對象,正如我之前提到的,它帶有一個名為 dataTransfer 的屬性,該屬性保存正在拖動的數據,並提供五個屬性:

  • dropEffect
  • effectAllowed
  • files
  • items(只讀)
  • types(只讀)

拖曳事件開始時,您可以進行一些操作。

設置/獲取效果

您可以在 dragstart 事件中通過設置 effectAllowed 屬性來設置拖動操作的期望效果。您有一些選項,用於設置放置目標應如何處理放下的元素:

  • none:不應該放下
  • move:可以移動
  • copy:可以複製
  • link:可以鏈接
  • copyMove:可以複製或移動
  • copyLink:可以複製或鏈接
  • linkMove:可以移動或鏈接
  • all:可以複製、移動或鏈接

(全部都是字串)

默認值為 all

dropEffect 屬性用於獲取拖放操作的類型,這次是由用戶通過使用修改鍵設置的。例如,在 Mac 上按下 Alt 鍵會將放置目標設置為複製項目而不是移動它。

此屬性不是只讀的。我们可以在 dragenterdragover 事件中編輯它,將其設置為以下字符串之一:

  • none:不應該放下
  • move:可以移動
  • copy:可以複製
  • link:可以鏈接

示例:

element.addEventListener('dragenter', event => {
 event.dataTrasfer.dropEffect = 'move'
})

正在傳輸的數據

您可以從 dataTransfer.items 屬性中訪問正在傳輸的項目,它是一個類似數組的對象,您可以使用循環迭代並訪問每個 DataTransferItem 對象。

DataTransferItem 有2個只讀屬性:

  • kind:正在拖動的項目的種類。返回包含 filestring 的字符串
  • type:項目的 MIME 類型

它還有2個方法:

  • getAsFile():返回表示正在拖動的數據的 File 對象
  • getAsString():將項目作為字符串傳遞給回調函數

它們具有相似的名稱,但工作方式非常不同。第一個返回一個 File 對象:

element.addEventListener('dragenter', event => {
 for (item of event.dataTrasfer.items) {
 const theFile = item.getAsFile()
 }
})

第二個將項目作為字符串傳遞給回調函數:

element.addEventListener('dragenter', event => {
 for (item of event.dataTrasfer.items) {
 item.getAsString(theString => {
 console.log(theString)
 })
 }
})

正在拖動的文件類型存儲在 dataTransfer 對象的 types 屬性中。它是一個包含字符串 string 的數組。如果我們正在拖動一個文件,則相應的類型是一個值為 Files 的字符串。

如果正在傳輸文件,則除了在 dataTransfer.items 中列出外,它們還存儲在 dataTransferfiles 屬性中。

此屬性指向一個 FileList 對象,列出了正在拖動的文件。

在 Codepen 上查看此演示:

參見 CodePen 上的拖放示例,作者是 Flavio Copes(@flaviocopes)。