HTML5原生拖拽/拖放 Drag & Drop 詳解

weiqinl發表於2019-03-03

前言

拖放(drap && drop)在我們平時的工作中,經常遇到。它表示:抓取物件以後拖放到另一個位置。目前,它是HTML5標準的一部分。我從幾個方面學習並實踐這個功能。

拖放的流程對應的事件

我們先看下拖放的流程:

選中 --->  拖動  ---> 釋放複製程式碼

然後,我們一步步看下這個過程中,會發生的事情。

選中

在HTML5標準中,為了使元素可拖動,把draggable屬性設定為true。
文字、圖片和連結是預設可以拖放的,它們的draggable屬性自動被設定成了true。
圖片和連結按住滑鼠左鍵選中,就可以拖放。
文字只有在被選中的情況下才能拖放。如果顯示設定文字的draggable屬性為true,按住滑鼠左鍵也可以直接拖放。

draggable屬性:設定元素是否可拖動。語法:<element draggable="true | false | auto" >

  • true: 可以拖動
  • false: 禁止拖動
  • auto: 跟隨瀏覽器定義是否可以拖動

拖動

每一個可拖動的元素,在拖動過程中,都會經歷三個過程,拖動開始-->拖動過程中--> 拖動結束

針對物件 事件名稱 說明
被拖動的元素 dragstart 在元素開始被拖動時候觸發
drag 在元素被拖動時反覆觸發
dragend 在拖動操作完成時觸發
目的地物件 dragenter 當被拖動元素進入目的地元素所佔據的螢幕空間時觸發
dragover 當被拖動元素在目的地元素內時觸發
dragleave 當被拖動元素沒有放下就離開目的地元素時觸發

dragenter和dragover事件的預設行為是拒絕接受任何被拖放的元素。因此,我們必須阻止瀏覽器這種預設行為。e.preventDefault();

釋放

到達目的地之後,釋放元素事件

針對物件 事件名稱 說明
目的地物件 drop 當被拖動元素在目的地元素裡放下時觸發,一般需要取消瀏覽器的預設行為。

選中拖動釋放例子

<!DOCTYPE HTML>
<html>

<head>
    <title>拖放示例-文字</title>
</head>
<style>
.src {
    display: flex;
}

.dropabled {
    flex: 1;
}

.txt {
    color: green;
}

.img {
    width: 100px;
    height: 100px;
    border: 1px solid gray;
}

.target {
    width: 200px;
    height: 200px;
    line-height: 200px;
    text-align: center;
    border: 1px solid gray;
    color: red;
}
</style>

<body>
    <div class="src">
        <div class="dragabled">
            <div class="txt" id="txt">
                所有的文字都可拖拽。
                <p draggable="true">此段文字設定了屬性draggable="true"</p>  
            </div>
            <div class="url" id="url">
                <a href="http://weiqinl.com" target="_blank">我是url:http://weiqinl.com</a>
            </div>
            <img class="img" id="tupian1" src="img1.png" alt="圖片1" />
            <img class="img" id="tupian2" src="img2.png" alt="圖片2" />
        </div>
        <div id='target' class="dropabled target">Drop Here</div>
    </div>
    <script>
        var dragSrc = document.getElementById('txt')
        var target = document.getElementById('target')

        dragSrc.ondragstart = handle_start
        dragSrc.ondrag = handle_drag
        dragSrc.ondragend = handle_end

        function handle_start(e) {
          console.log('dragstart-在元素開始被拖動時候觸發')
        }

      function handle_drag() {
            console.log('drag-在元素被拖動時候反覆觸發')
        }

      function handle_end() {
            console.log('dragend-在拖動操作完成時觸發')
        }


        target.ondragenter = handle_enter
        target.ondragover = handle_over
        target.ondragleave = handle_leave

        target.ondrop = handle_drop

        function handle_enter(e) {
            console.log('handle_enter-當元素進入目的地時觸發')
            // 阻止瀏覽器預設行為
            e.preventDefault()
        }

        function handle_over(e) {
            console.log('handle_over-當元素在目的地時觸發')
            // 阻止瀏覽器預設行為
            e.preventDefault()
        }

        function handle_leave(e) {
            console.log('handle_leave-當元素離開目的地時觸發')
            // 阻止瀏覽器預設行為
            // e.preventDefault()
        }

        function handle_drop(e) {
            console.log('handle_drop-當元素在目的地放下時觸發')
            var t = Date.now()
            target.innerHTML = ''
            target.append(t + '-拖放觸發的事件。')
            e.preventDefault()
        }
    </script>
</body>

</html>複製程式碼

drag-drop事件觸發

在整個拖放過程中,我們以上說的是表面現象,事件過程內部還會發生什麼事情呢?請看下面?的DataTransfer物件。

DataTransfer物件

與拖放操作所觸發的事件同時派發的物件是DragEvent,它派生於MouseEvent,具有Event與MouseEvent物件的所有功能,並增加了dataTransfer屬性。該屬性用於儲存拖放的資料和互動資訊,返回DataTransfer物件。
// DataTransfer dataTransfer = DragEvent.dataTransfer
DataTransfer物件定義的屬性和方法有很多種,我們看下列入標準的幾個。

屬性 說明
types 只讀屬性。它返回一個我們在dragstart事件中設定的拖動資料格式的陣列。 格式順序與拖動操作中包含的資料順序相同。IE10+、Edge、safari3.1、Firefox3.5+ 和Chrome4以上支援該屬性
files 返回拖動操作中的檔案列表。包含一個在資料傳輸上所有可用的本地檔案列表。如果拖動操作不涉及拖動檔案,此屬性是一個空列表。
dropEffect 獲取當前選定的拖放操作的型別或將操作設定為新型別。它應該始終設定成effectAllowed的可能值之一【none、move、copy、link】。dragover事件處理程式中針對放置目標來設定dropEffect。
effectAllowed 指定拖放操作所允許的效果。必須是其中之一【 none, copy, copyLink, copyMove, link, linkMove, move, all, uninitialized】預設為uninitialized 表示允許所有的效果。ondragstart處理程式中設定effectAllowed屬性
方法 說明
void setData(format, data) 將拖動操作的拖動資料設定為指定的資料和型別。format可以是MIME型別
String getData(format) 返回指定格式的資料,format與setData()中一致
void clearData([format]) 刪除給定型別的拖動操作的資料。如果給定型別的資料不存在,此方法不執行任何操作。如果不給定引數,則刪除所有型別的資料。
void setDragImage(img, xOffset, yOffset) 指定一副影像,當拖動發生時,顯示在游標下方。大多數情況下不用設定,因為被拖動的節點被建立成預設圖片。x,y引數分別指示影像的水平、垂直偏移量
//IE10及之前版本,不支援擴充套件的MIME型別名
//Firefox 5版本之前,不能正確的將url和text對映為text/uri-list 和text/plain
var dataTransfer = event.dataTransfer;
//讀取文字,
var text = dataTransfer.getData("Text");
//讀取URL,
var url = dataTransfer.getData("url") || dataTransfer.getData("text/uri-list");複製程式碼

drag-drop-dataTransfer各屬性方法示例

瀏覽器支援程度

說了這麼多,如果瀏覽器不支援,也是白扯。

Method of easily dragging and dropping elements on a page, requiring minimal JavaScript.
要求最少的js,實現拖拽頁面元素的簡單方法

Drag and Drop 瀏覽器相容性.png
Drag and Drop 瀏覽器相容性.png

drag之瀏覽器支援程度--caniuse

note

  • dataTransfer.items 只有Chrome支援
  • dropzone屬性,目前沒有瀏覽器支援
  • Firefox支援.setDragImage任何型別的DOM元素。Chrome必須有HTMLImageElement或者任何DOM元素,該DOM元素附加到DOM 和瀏覽器的.setDragImage視口(viewport)內。
    1.部分支援是指不支援dataTransfer.files 或者 .types物件
    2.部分支援是指不支援.setDragImage
    3.部分支援是指dataTransfer.setData / getData 的有限支援格式

以下,我在實際中遇到的情況,各瀏覽器對標準的實現還是有差異的。

  • getData()在chrome 62.0瀏覽器中,只能在drop事件中生效。
  • 如果使用setDragImage方法,指定的影像不存在,則拖動過程:
    1. safari 11.0.1 瀏覽器,只會觸發dragstartdragend事件。
    2. chrome、opera 和 Firefox會正常觸發其他事件。
  • 每一次拖放操作,Firefox都會執行一次新開一個頁面的動作,並且自動搜尋dataTransfer.getData()得到的內容。
    解決方法,在drop事件中,新增: e.stopPropagation();// 不再派發事件。解決Firefox瀏覽器,開啟新視窗的問題
  • opera 49版本中,連結預設不可以拖動,必須把draggable屬性設定為true,才可以拖動。
  • 關於 dropEffecteffectAllowed
    1. effectAllowed 允許拖放操作的效果,最多不會超過那麼幾種。dropEffect 設定拖放操作的具體效果,只能是四種可能之一。
    2. 如果effectAllowed設定為none,則不允許拖放元素。但是各個瀏覽器能觸發的事件不一樣。(注意:safari可以拖放元素,而且會觸發所有事件)
    3. 如果dropEffect設定為none,則不允許被拖放到目的地元素中。
    4. 如果設定了effectAllowed的值,那麼如果要設定dropEffect的值,其值必須和effectAllowed的值一致,否則拖動效果無效,而且不允許將被拖放元素放到目的地元素中。(注:safari11.0.1有效果,而且也能拖動到目的地元素中,但是這不符合標準)。

示例

drag-drop-dataTransfer各屬性方法示例
drag-drop事件觸發

總結

原生HTML5拖拽API,drag && drop 在實際工作中,還是有很多情況下會遇到的。
以上,我只介紹了部分常用API。API不復雜,多看會兒,實踐就知道了。各個瀏覽器,可能會在表現上,稍有不同,但我相信大家還是會向著標準發展的。
順帶說一句,第一次在掘金寫文章,markdown編輯器棒棒的?。
部落格園地址:www.cnblogs.com/weiqinl

相關文章