拖動效果相信很多朋友都自己寫過,不管你用原生JS還是Jquery要實現起來也很簡單,但是今天我想介紹的是運用HTML5標准中定義的原生拖動事件實現拖動效果。
一、背景:
其實說是HTML5標准定義,其實最早在IE4中就有拖放功能的API,只是在IE4中,網頁中只有兩種對象可以拖放:圖像和某些文本。並且在IE4中唯一有效的放置目標是文本框。到了IE5.5,拖放功能得到了擴展,讓網頁中的任何元素都可以拖放。最終HTML5以IE的實力為基礎制定了拖放規范。FF3.5+,Safari3+和Chrome根據規范實現了原生拖放。
二、事件:
原生拖放中涉及到的事件分為分下列兩個階段:
(1)拖動某元素時,該階段會依次觸發下列事件(該階段的事件目標--即target或srcElement都是這個被拖動元素):
1>dragstart——鼠標移入目標元素並且按下左鍵觸發。
2>drag——dragstart觸發后移動鼠標連續觸發該事件(類似mousemove事件)
3>dragend——拖動停止時觸發(無論此時拖動元素在有效位置還是無效位置)。
(2)當元素被拖動到一個有效的放置目標上時,觸發下列事件(該階段的事件目標--即target或srcElement都是這個目標元素):
1>dragenter——只要有元素被拖動到放置目標上,就觸發dragenter事件(類似mouseover)
2>dragover——觸發dragenter后在有效目標范圍內移動時連續觸發該事件
3>dragleave——被拖動元素從目標范圍內被拖出到目標范圍外時觸發
4>drop——被拖動元素被放到了目標范圍內(即在有效目標范圍內松口鼠標左鍵)
注意:這里dragleave和drop二者只能觸發其一!
三、定義放置位置:
雖然所有元素都支持放置目標事件,但是這些元素默認情況下是不允許放置的,如果拖動元素經過不允許放置的元素,那無論怎樣都無法觸發drop事件。所 以我們必須先定義出放置的目標元素。
定義目標元素也很簡單,只要阻止dragenter和dragover事件的默認行為就可以了,假設我們的目標元素為一個id為drag-area的div,我們要把它定義成我 們的目標元素,只需下面代碼就可以了:
drag_area.ondragover=function(evt){ //阻止dragover的默認事件 var evt=evt || window.event; if(typeof evt.preventDefault=="function"){ evt.preventDefault(); }else{ evt.returnValue=false; } } drag_area.ondragenter=function(evt){ //阻止dragenter的默認事件 var evt=evt || window.event; if(typeof evt.preventDefault=="function"){ evt.preventDefault(); }else{ evt.returnValue=false; } }
(有小伙伴可能會問了,啊~阻止默認事件不是可以直接用return false么,干嘛這么麻煩。的確,return false可以阻止默認事件和事件傳播,而且我測試過,用return false;的確也可以達到相同效果。這里主要是firefox的一個問題,我的firefox是最新版本,直接用return false沒有問題,但是據 http://blog.csdn.net/goldlevi/article/details/5721348 說,稍早一些的firefox版本如果dragenter和dragover沒有設置preventDefault的話不會在該元素上觸發drag事件的,偷個懶,就不自己測試其他firefox了~關於FF還有一點問題,后面會提到);
四.實現
前面解釋了一下自定義放置目標,這里具體的實現就不多說了(其實原理和普通實現差不多~)直接上代碼:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html,charset=utf-8"> <title></title> <style type="text/css"> #drag-area{ width: 500px;height:500px; border: 1px solid red; position: relative; } #my-img{ position: absolute; } </style> </head> <body> <div id="drag-area"> <img src="pity.gif" alt="" id="my-img"> </div> </body> <script type="text/javascript"> var my_img=document.getElementById("my-img"); var drag_area=document.getElementById("drag-area"); my_img.ondragstart=function(evt){ //在被拖動的元素的dragstart事件中取得坐標便宜 var evt=evt || window.event; //即代碼中evt.clientX-this.offsetLeft和evt.clientY-this.offsetTop,並保存在dataTransfer對象中 evt.dataTransfer.setData("text",(evt.clientX-this.offsetLeft)+";"+(evt.clientY-this.offsetTop)); } drag_area.ondragover=function(evt){ //阻止dragover的默認事件 var evt=evt || window.event; if(typeof evt.preventDefault=="function"){ evt.preventDefault(); }else{ evt.returnValue=false; } } drag_area.ondragenter=function(evt){ //阻止dragenter的默認事件 var evt=evt || window.event; if(typeof evt.preventDefault=="function"){ evt.preventDefault(); }else{ evt.returnValue=false; } } drag_area.ondrop=function(evt){ var evt=evt || window.event; var drag_data=evt.dataTransfer.getData("Text").split(";");//從dataTransfer對象中取出數據,並將字符串分割成數組 var offset_x=drag_data[0],//取得橫向偏移 offset_y=drag_data[1];//取得縱向偏移 if(typeof evt.preventDefault=="function"){ //阻止drop事件的默認行為 evt.preventDefault(); }else{ evt.returnValue=false; } my_img.style.left=(evt.clientX-offset_x)+"px";//給拖動元素的left,top賦值 my_img.style.top=(evt.clientY-offset_y)+"px"; } </script> </html>
代碼限定了被拖動元素(my_img)只能在(drag_area)這個500*500的div里面拖動。代碼中我們在拖放元素的dragstart事件中取得了鼠標點擊時的坐標偏移evt.clientX-this.offsetLeft和evt.clientY-this.offsetTop然后將其存放在dataTransfer對象中,
然后又在目標元素的drop事件里從dataTransfer對象中取出了偏移值,用來計算拖動元素的left和top。
解釋下dataTransfer這個對象。該對象是事件對象的一個屬性,通常我們在利用它在被拖動元素(如代碼中的my_img)dragstart事件處理程序中設置它的值,在目標元素(代碼中的drag_area)drop事件(並且只能在drop事件)中讀取其值,此外,瀏覽器在我們拖動文本(圖片/鏈接)時,會自動將拖動的文本(url)存放在該對象中。
設置dataTransfer內容的語法如下:
event.dataTransfer.setData("數據類型","值")——這里數據類型雖然HTML定義了多種MIME類型,但是最好根據情況只使用
‘text’—保存字符串和’URL‘—保存url(ie只定義了這兩種類型);
值當然就是你需要的值啦。
通過dataTransfer對象提取數據語法如下:
event.dataTransfer.getData("數據類型")——這里數據類型同setData中的數據類型。
另外dataTransfer對象還有兩個屬性,dropEffect和EffectAllowed以及一些其他屬性和方法,不過一般用不到,兼容性也不是很好,就不多說了。
最后補充一下,HTML5還為所有的HTML元素規定了一個draggable屬性,表示元素是否可以拖動。圖像和鏈接的draggable屬性默認是true,其他元素默認false。想要改變能否拖動,可以在標簽內設置這個屬性。eg:
<img src="test.gif" draggable="false">
這樣這個圖片就不能再拖拽了。同樣可設置其他元素draggable為true使其可以拖拽。
(實現draggable屬性的有IE10+,FF4+,safari5+,chrome)
好了,有興趣的同學把圖片鏈接換一下試試吧~該方法兼容性尚可:在IE6+,opera11.5+,chrome,safari5+中都沒問題,但是在FF中拖動圖片會自動打開圖片的url,而且阻止不了(代碼中drop事件有阻止默認代碼~測試版本ff31.0和ff32.03)=_=!根本不科學嘛~希望看到本文,有解決方法的大神留言。
總結一下,雖然我們用mousedown、mousemove、mouseup也能方便做出拖放效果,而且沒有上面說的兼容性和FF中的BUG,但是如果項目不需要兼容較早的opera,而且是拖拽選中文本的話,此方法就比較方便了(上面說過,拖拽文本瀏覽器會自動將選中並拖拽的文本、URL存放在dataTransfer對象中),另外,利用此API在高富帥瀏覽器里實現個什么拖拽上傳什么的也是很常見的應用場景。