不要搞錯,本文不是講如何拖地的。看過《javascript精粹》朋友應該知道,他實現拖放的過程比較復雜,現在時代不同了,我們用H5的新的拖放API就能非常方便的實現拖放效果了。最近在園子見一園友寫了一篇《HTML5 進階系列:拖放 API 實現拖放排序》,真乃大師之作,大~熊同學作為一代菜鳥(不是宗師),無法與之想比,遂推出基礎篇,希望各位園友有所收獲。
一、一個簡單的例子--地上有石子撿幾個吧
<!DOCTYPE html> <html> <head> <title>地上有石子,撿幾個吧</title> <meta charset="utf-8"> <style type="text/css"> .lanzi{ width: 400px; height: 200px; border:1px solid black; } .floor{ width: 500px; height: 80px; border: 1px solid black; } .shizi{ display: inline-block; width: 40px; height: 40px; background: #ccc; border-radius: 20px; margin-top: 40px; text-align: center; line-height: 40px; } </style> </head> <body> <p>從前有個人走在叢林里,傳來一個聲音,說地上有石子,撿幾個吧,會有用的。那個人不以為然,撿了一個,但是走出從林后發現手中的石子變成了金子...(
這個故事充滿了哲學智慧),下面我們要給出一些石子,希望你能把它見到籃子里,沒准編程金子哦!! </p> <h4>我是籃子</h4> <div class="lanzi" id="lanzi"></div> <h4>我是地</h4> <div class="floor" id="floor"> <span class="shizi" draggable="true" data-id="1">石子</span> <span class="shizi" draggable="true" data-id="2">石子</span> <span class="shizi" draggable="true" data-id="3">石子</span> <span class="shizi" draggable="true" data-id="4">石子</span> <span class="shizi" draggable="true" data-id="5">石子</span> <span class="shizi" draggable="true" data-id="6">石子</span> <span class="shizi" draggable="true" data-id="7">石子</span> <span class="shizi" draggable="true" data-id="8">石子</span> <span class="shizi" draggable="true" data-id="9">石子</span> <span class="shizi" draggable="true" data-id="10">石子</span> </div> <script type="text/javascript"> window.addEventListener("load",handler,false); function handler(){ var floor = document.getElementById("floor"); var lanzi = document.getElementById("lanzi"); var shizi; floor.addEventListener("dragstart",function(e){ if(e.target.className == "shizi"){ shizi = e.target; var dt = e.dataTransfer; dt.effectedAllowed = "all"; } },false); lanzi.addEventListener("dragend",function(e){e.preventDefault()},false); lanzi.addEventListener("drop",function(e){ var data = e.dataTransfer.getData("text/plain"); lanzi.appendChild(shizi); e.preventDefault(); e.stopPropagation(); },false); document.ondragover = function(e){e.preventDefault();} document.ondrop = function(e){e.preventDefault();} } </script> </body> </html>
(gif演示是用的edge,我的ubuntu做gif太麻煩了,借了個windows)
這里用一個簡單的例子演示了如何實現拖放,那么問題來了,從上面的演示中你可以猜出一些屬性和方法的用法了,那些方法的作用究竟是怎樣的?那些個屬性又是啥意思呢?下面一一到來。
二、實現拖放的一般步驟
1、找到一個可拖的元素
正如不是所有人都叫大熊一樣,並不是所有的元素都是可以被拖的。img和a元素默認可拖,其他元素默認不可拖,當時可以加一個draggable=true讓它可拖。
<div draggable='true'></div>
2、處理拖放有關事件
所有相關事件如下:(這個摘自:http://www.cnblogs.com/linxin/p/6794542.html)
源對象:
- dragstart:源對象開始拖放。
- drag:源對象拖放過程中。
- dragend:源對象拖放結束。
過程對象:
- dragenter:源對象開始進入過程對象范圍內。
- dragover:源對象在過程對象范圍內移動。
- dragleave:源對象離開過程對象的范圍。
目標對象:
- drop:源對象被拖放到目標對象內。
我們可以用一個測試來看看這些事件的觸發時機和事件對象為何物。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>testEvents</title> <style type="text/css"> .source{ width: 50px; height: 50px; border: 1px solid red; } .process{ width: 100px; height: 100px; border: 1px solid black; margin-top: 10px; } .dest{ width: 100px; height: 100px; border: 1px solid green; margin-top: 10px; } </style> </head> <body> <div class="source" id="source" draggable="true"></div> <div class="process" id="process"></div> <div class="dest" id="dest"></div> <script type="text/javascript"> window.onload=function(){ var source = document.getElementById("source"); var process = document.getElementById("process"); var dest = document.getElementById("dest"); var sourceEle; source.addEventListener("dragstart",function(e){ console.log("source dragstart"); console.log(e); sourceEle = e.target; var dt = e.dataTransfer; dt.effectedAllowed = "all"; },false); process.addEventListener("dragenter",function(e){ console.log("process dragenter"); console.log(e); },false); process.addEventListener("dragover",function(e){ console.log("process dragover"); console.log(e); },false); process.addEventListener("dragleave",function(e){ console.log("process dragleave"); console.log(e); },false); source.addEventListener("drag",function(e){ console.log("source drag"); console.log(e); },false); dest.addEventListener("dragend",function(e){ console.log("dest dragend"); console.log(e); e.preventDefault(); },false); dest.addEventListener("drop",function(e){ console.log("dest drop"); console.log(e); dest.appendChild(sourceEle); e.preventDefault(); e.stopPropagation(); },false); document.ondragover = function(e){e.preventDefault();} document.ondrop = function(e){e.preventDefault();} } </script> </body> </html>
這個例子將拖放過過程涉及的事件做了一個羅列,這里不再細講,你可以查看控制台看看事件的觸發順序和事件對象。
三、一個重要的對象DataTransfer對象
這里首字母大寫了,嚴格說叫做一個類,每一次拖放會實例化這個類,保存在事件對象的dataTransfer屬性中。其屬性和方法見下表(來自:http://www.cnblogs.com/ijjyo/p/4316232.html)
感謝這位兄台的總結,拿了你這么多東西,謝謝啊。
下面做一些簡單的測試
關於effectAllowed和dropEffect,這里將前者置為effectAllowed后者用下拉列表選擇,以便於看到不同的鼠標樣式。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>testEvents</title> <style type="text/css"> .source{ width: 50px; height: 50px; border: 1px solid red; } .process{ width: 100px; height: 100px; border: 1px solid black; margin-top: 10px; } .dest{ width: 100px; height: 100px; border: 1px solid green; margin-top: 10px; } </style> </head> <body> <select id="dpe"> <option value="copy">copy</option> <option value="move">move</option> <option value="link">link</option> <option value="none">none</option> </select> <div class="source" id="source" draggable="true"></div> <div class="process" id="process"></div> <div class="dest" id="dest"></div> <script type="text/javascript"> window.onload=function(){ var source = document.getElementById("source"); var process = document.getElementById("process"); var dest = document.getElementById("dest"); var dpe = document.getElementById("dpe"); var dpev; dpe.onchange = function(){ dpev = this.value; } var sourceEle; source.addEventListener("dragstart",function(e){ console.log("source dragstart"); console.log(e); sourceEle = e.target; var dt = e.dataTransfer; dt.effectedAllowed = "all"; },false); dest.addEventListener("dragend",function(e){ console.log("dest dragend"); console.log(e); e.preventDefault(); },false); dest.addEventListener("drop",function(e){ console.log("dest drop"); console.log(e); dest.appendChild(sourceEle); e.preventDefault(); e.stopPropagation(); },false); document.ondragover = function(e){ e.dataTransfer.dropEffect = dpev; e.preventDefault(); } document.ondrop = function(e){e.preventDefault();} } </script> </body> </html>
我在ubuntu chrome測試法現,都是一個手,但是置為none時拖不進去了,可能不同系統會有一些差別。
關於setData()和getData()方法
這兩個是有關數據交換的方法,前者傳兩個參數,第一參數是一個mime類型字符串,第二個是數據;后者傳一個參數,為mime類型。可用mime類型為text/plain,text/html,text/xml,text/uri-list.
測試用例,將菜單的菜拖到記錄本上。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>點菜</title> <style type="text/css"> .menu{ width: 200px; height: 300px; border: 1px solid red; margin-right: 10px; float: left; } .record{ width: 200px; height: 300px; border: 1px solid black; margin-right: 10px; float: left; } </style> </head> <body> <ul class="menu" id="menu"> <li draggable="true">糖醋排骨</li> <li draggable="true">青椒肉絲</li> <li draggable="true">武昌魚</li> <li draggable="true">手撕包材</li> <li draggable="true">千葉豆腐</li> </ul> <ul class="record" id="record"> </ul> <script type="text/javascript"> window.onload = function(){ var menu = document.getElementById("menu"); var record = document.getElementById("record"); menu.addEventListener("dragstart",function(e){ var dt = e.dataTransfer; var tar = e.target; if(tar.tagName=="LI"){ dt.setData("text/plain",tar.innerHTML); } dt.effectedAllowed = "all"; },false); record.addEventListener("drop",function(e){ var li = document.createElement("li"); li.appendChild(document.createTextNode(e.dataTransfer.getData("text/plain"))); record.appendChild(li); e.stopPropagation(); },false); record.addEventListener("dropend",function(e){ e.preventDefault(); },false); document.addEventListener("dragover",function(e){e.preventDefault()},false); document.addEventListener("drop",function(e){e.preventDefault()},false); } </script> </body> </html>
關於setDragImage(Element img,long x,long y)
這個方法是設置拖放時的圖標的,第一個參數表是圖標元素,第二個表示相對與光標的水平偏移,第三個是垂直的。
還是前面的例子,在dragstart事件添加下面的代碼,拖動時你會發現一只很大的手(不要被嚇到);
var img = document.createElement("img"); img.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493802056263&di=232de2c30491e19f32833669ad5a67ae&imgtype=0&src=http%3A%2F%2Fstatic.freepik.com%2Ffree-photo%2Fone-finger_318-10333.jpg"; dt.setDragImage(img,10,10);
四、關於拖放數據傳遞
上面的例子已經談到了拖放的數據傳遞方法,這里在總結一下。
1、通過dataTransfer的setData()和getData()方法傳遞
2、通過閉包的方法,請參考開篇的例子。
五、總結
HTML5的拖放api非常簡潔實用,為我們省去了很多麻煩,如果沒有它,我們可能需要通過mousedownmousemove等等事件才能實現上述功能。本文較為詳細的介紹了相關api,希望對你有所幫助。關於拖放api的應用大家可以參看下面鏈接的文章,他做了一個拖放排序,這是一個比較常見的應用場景。
大~熊同學的粉絲數正在逼近三位數,感謝各位園友的支持,大~熊會繼續努力的!
參考: