談?wù)剬avaScript原生拖放的深入理解
前面的話
拖放(drag-and-drop,DnD)其實(shí)是兩個(gè)動(dòng)作——拖和放。所以,它涉及到兩個(gè)元素。一個(gè)是被拖的元素,稱為拖放源;另一個(gè)是要放的目標(biāo),稱為拖放目標(biāo)。本文將通過拆分這兩個(gè)概念來詳細(xì)介紹原生拖放
拖放源
什么樣的元素才是拖放源呢?
HTML5為所有HTML元素規(guī)定了一個(gè)draggable屬性,表示元素是否可以拖動(dòng)
圖像和鏈接的draggable屬性自動(dòng)被設(shè)置成了true,而其他元素這個(gè)屬性的默認(rèn)值都是false
[注意]必須設(shè)置draggable='true'才能生效,只設(shè)置draggable不起作用
默認(rèn)情況下,文本只有在被選中的情況下才能拖動(dòng),而圖像和鏈接在任何時(shí)候都可以拖動(dòng)。而其他元素則無法被拖放
<input value="文字可拖動(dòng)"> <img alt="圖像可拖動(dòng)" src="http://files.cnblogs.com/files/xiaohuochai/zan.gif"> <a href="#">鏈接可拖動(dòng)</a> <div id="test" style="height:30px;width:300px;background:pink;">元素不可拖動(dòng)</div>
當(dāng)為元素設(shè)置draggable屬性后,普通元素也可以拖動(dòng)
<div draggable="true" style="height:30px;width:100px;background:pink;"></div>
兼容
IE9-瀏覽器不支持draggable屬性,但可通過mousedown事件處理程序調(diào)用dragDrop()方法來實(shí)現(xiàn)拖動(dòng)效果
<div id="test" style="height:30px;width:300px;background:pink;"></div> <script> test.onmousedown = function(){ this.dragDrop(); } </script>
[注意]如果讓firefox支持draggable屬性,必須添加一個(gè)ondragstart事件處理程序,并在dataTransfer對象使用setData()方法來啟動(dòng)效果
拖放事件
拖放源涉及到3個(gè)拖放事件。拖動(dòng)拖放源時(shí),依次觸發(fā)dragstart、drag和dragend這3個(gè)事件
dragstart
按下鼠標(biāo)鍵并開始移動(dòng)鼠標(biāo)時(shí),會(huì)在被拖放的元素上觸發(fā)dragstart事件。此時(shí)光標(biāo)變成“不能放”符號(圓環(huán)中有一條反斜線),表示不能把元素放到自己上面
drag
觸發(fā)dragstart事件后,隨即會(huì)觸發(fā)drag事件,而且在元素被拖動(dòng)期間會(huì)持續(xù)觸發(fā)該事件
dragend
當(dāng)拖動(dòng)停止時(shí)(無論是把元素放到了有效的放置目標(biāo),還是放到了無效的放置目標(biāo)上),會(huì)觸發(fā)dragend事件
<div id="test" draggable="true" style="height:30px;width:100px;background:pink;">0</div> <script> var timer,i=0; test.ondragstart = function(){ this.style.backgroundColor = 'lightgreen'; } test.ondrag = function(){ if(timer) return; timer = setInterval(function(){ test.innerHTML = i++; },100) } test.ondragend = function(){ clearInterval(timer); timer = 0; this.style.backgroundColor = 'pink'; } </script>
拖放目標(biāo)
拖放目標(biāo)是指被拖動(dòng)的元素松開鼠標(biāo)時(shí)被放置的目標(biāo)
拖放源被拖動(dòng)到拖放目標(biāo)上時(shí),將依次觸發(fā)dragenter、dragover和dragleave或drop這四個(gè)事件
dragenter
只要有元素被拖動(dòng)到放置目標(biāo)上,觸發(fā)dragenter事件
dragover
被拖動(dòng)的元素在放置目標(biāo)的范圍內(nèi)移動(dòng)時(shí),持續(xù)觸發(fā)dragover事件
dragleave
如果元素被拖出了放置目標(biāo),觸發(fā)dragleave事件
drop
如果元素被放到了放置目標(biāo)中,觸發(fā)drop事件
[注意]firefox瀏覽器的drop事件的默認(rèn)行為是打開被放到放置目標(biāo)上的URL。為了讓firefox支持正常的拖放,還要取消drop事件的默認(rèn)行為
默認(rèn)情況下,目標(biāo)元素是不允許被放置的,所以不會(huì)發(fā)生drop事件。只要在dragover和dragenter事件中阻止默認(rèn)行為,才能成為被允許的放置目標(biāo),才能允許發(fā)生drop事件。此時(shí),光標(biāo)變成了允許放置的符號
<div id="test" draggable="true" style="height:30px;width:130px;background:pink;float:left;">拖放源</div> <div id="target" style="float:right;height: 200px;width:200px;background:lightblue;">拖放目標(biāo)</div> <script> var timer,i=0; var timer1,i1=0; //兼容IE8-瀏覽器 test.onmousedown = function(){ if(this.dragDrop){ this.dragDrop(); } } test.ondragstart = function(){ this.style.backgroundColor = 'lightgreen'; this.innerHTML = '開始拖動(dòng)'; } test.ondrag = function(){ if(timer) return; timer = setInterval(function(){ test.innerHTML = '元素已被拖動(dòng)' + ++i + '秒'; },1000); } test.ondragend = function(){ clearInterval(timer); timer = 0;i =0; this.innerHTML = '結(jié)束拖動(dòng)'; this.style.backgroundColor = 'pink'; } target.ondragenter = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } this.innerHTML = '有元素進(jìn)入目標(biāo)區(qū)域'; this.style.background = 'red'; } target.ondragover = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } if(timer1) return; timer1 = setInterval(function(){ target.innerHTML = '元素已進(jìn)入' + (++i1) + '秒'; },1000); } target.ondragleave = function(){ clearInterval(timer1); timer1 = 0;i1=0; this.innerHTML = '元素已離開目標(biāo)區(qū)域'; this.style.backgroundColor = 'lightblue'; } target.ondrop = function(){ clearInterval(timer1); timer1 = 0;i1=0; this.innerHTML = '元素已落在目標(biāo)區(qū)域'; this.style.backgroundColor = 'orange'; } </script>
dataTransfer對象
為了在拖放操作時(shí)實(shí)現(xiàn)數(shù)據(jù)交換,引入了dataTransfer對象,它是事件對象的一個(gè)屬性,用于從被拖動(dòng)元素向放置目標(biāo)傳遞字符串格式的數(shù)據(jù)
dataTransfer對象有兩個(gè)主要方法:getData()和setData()
getData()可以取得由setData()保存的值。setData()方法的第一個(gè)參數(shù),也是getData()方法唯一的一個(gè)參數(shù),是一個(gè)字符串,表示保存的數(shù)據(jù)類型,取值為"text"或"URL"
IE只定義了"text"和"URL"兩種有效的數(shù)據(jù)類型,而HTML5則對此加以擴(kuò)展,允許指定各種MIME類型。考慮到向后兼容,HTML5也支持"text"和"URL",但這兩種類型會(huì)被映射為"text/plain"和"text/uri-list"
實(shí)際上,dataTransfer對象可以為每種MIME類型都保存一個(gè)值。換句話說,同時(shí)在這個(gè)對象中保存一段文本和一個(gè)URL不會(huì)有任何問題
[注意]保存在dataTransfer對象中的數(shù)據(jù)只能在drop事件處理程序中讀取
在拖動(dòng)文本框中的文本時(shí),瀏覽器會(huì)調(diào)用setData()方法,將拖動(dòng)的文本以"text"格式保存在dataTransfer對象中。類似地,在拖放鏈接或圖像時(shí),會(huì)調(diào)用setData()方法并保存URL。然后,在這些元素被拖放到放置目標(biāo)時(shí),就可以通過getData()讀到這些數(shù)據(jù)
<div>請將從這堆內(nèi)容不同亂七八糟的文字中挑選一些移動(dòng)到拖放目標(biāo)中</div> <div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目標(biāo)</div> <div id="result"></div> <script> target.ondragenter = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } this.innerHTML = '有元素進(jìn)入目標(biāo)區(qū)域'; this.style.background = 'red'; } target.ondragover = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } } target.ondragleave = function(e){ e = e || event; this.innerHTML = '元素已離開目標(biāo)區(qū)域'; this.style.backgroundColor = 'lightblue'; } target.ondrop = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } result.innerHTML = '落入目標(biāo)區(qū)域的文字為:' + e.dataTransfer.getData('text'); this.innerHTML = '元素已落在目標(biāo)區(qū)域'; this.style.backgroundColor = 'orange'; } </script>
當(dāng)然,也可以在dragstart事件處理程序中調(diào)用setData(),手動(dòng)保存自己要傳輸?shù)臄?shù)據(jù),以便將來使用
<div id="test" draggable="true" data-value="這是一個(gè)秘密" style="height:30px;width:100px;background:pink;">拖動(dòng)源</div> <div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目標(biāo)</div> <div id="result"></div> <script> //兼容IE8-瀏覽器 test.onmousedown = function(){ if(this.dragDrop){ this.dragDrop(); } } test.ondragstart = function(e){ e = e || event; e.dataTransfer.setData('text',test.getAttribute('data-value')); } target.ondragenter = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } this.innerHTML = '有元素進(jìn)入目標(biāo)區(qū)域'; this.style.background = 'red'; } target.ondragover = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } } target.ondragleave = function(e){ e = e || event; this.innerHTML = '元素已離開目標(biāo)區(qū)域'; this.style.backgroundColor = 'lightblue'; } target.ondrop = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } result.innerHTML = '落入目標(biāo)區(qū)域的文字為:' + e.dataTransfer.getData('text'); this.innerHTML = '元素已落在目標(biāo)區(qū)域'; this.style.backgroundColor = 'orange'; } </script>
改變光標(biāo)
利用dataTransfer對象,不僅可以傳輸數(shù)據(jù),還能通過它來確定被拖動(dòng)的元素以及作為放罝目標(biāo)的元素能夠接收什么操作。為此,需要訪問dataTransfer對象的兩個(gè)屬性:dropEffect和effectAllowed
實(shí)際上,這兩個(gè)屬性并沒有什么用,只是拖動(dòng)源在拖動(dòng)目標(biāo)上移動(dòng)時(shí),改變不同的光標(biāo)而已(但是,有一種情況除外)
dropEffect
dropEffect屬性可以知道被拖動(dòng)的元素能夠執(zhí)行哪種放置行為。這個(gè)屬性有下列4個(gè)可能的值
"none":不能把拖動(dòng)的元素放在這里。這是除文本框之外所有元素的默認(rèn)值(此時(shí),將無法觸發(fā)drop事件)
"move":應(yīng)該把拖動(dòng)的元素移動(dòng)到放置目標(biāo)
"copy":應(yīng)該把拖動(dòng)的元素復(fù)制到放置目標(biāo)
"link":表示放置目標(biāo)會(huì)打開拖動(dòng)的元素(但拖動(dòng)的元素必須是一個(gè)鏈接,有URL)
在把元素拖動(dòng)到放置目標(biāo)上時(shí),以上每一個(gè)值都會(huì)導(dǎo)致光標(biāo)顯示為不同的符號
[注意]必須在ondragover事件處理程序中針對放置目標(biāo)來設(shè)置dropEffect屬性
effectAllowed
dropEffect屬性只有搭配effectAllowed屬性才有用。effectAllowed屬性表示允許拖動(dòng)元素的哪種dropEffect
effectAllowed屬性可能的值如下
"uninitialized":沒有給被拖動(dòng)的元素設(shè)置任何放置行為
"none":被拖動(dòng)的元素不能有任何行為
"copy":只允許值為"copy"的dropEffect
"link"只允許值為"link"的dropEffect
"move":只允許值為"move"的dropEffect
"copyLink":允許值為"copy"和"link"的dropEffect
"copyMove":允許值為"copy"和"move"的dropEffect
"linkMove":允許值為"link"和"move"的dropEffect
"all":允許任意dropEffect
[注意]必須在ondragstart事件處理程序中設(shè)置effectAllowed屬性
<div id="test" draggable="true" style="height:30px;width:100px;background:pink;display:inline-block;">拖放源</div> <br> <div id="target1" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(none)拖放目標(biāo)</div> <div id="target2" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(move)拖放目標(biāo)</div> <div id="target3" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(copy)拖放目標(biāo)</div> <div id="target4" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(link)拖放目標(biāo)</div> <div id="result"></div> <script> //兼容IE8-瀏覽器 test.onmousedown =function(){ if(this.dragDrop){ this.dragDrop(); } } test.ondragstart = function(e){ e = e || event; //兼容firefox瀏覽器 e.dataTransfer.setData('text',''); e.dataTransfer.effectAllowed = 'all'; } target1.ondragenter = target2.ondragenter =target3.ondragenter =target4.ondragenter =function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; }this.style.background = 'red'; } target1.ondragover = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } e.dataTransfer.dropEffect = 'none'; } target2.ondragover = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } e.dataTransfer.dropEffect = 'move'; } target3.ondragover = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } e.dataTransfer.dropEffect = 'copy'; } target4.ondragover = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } e.dataTransfer.dropEffect = 'link'; } target1.ondragleave = target2.ondragleave =target3.ondragleave =target4.ondragleave =function(e){ e = e || event; this.style.backgroundColor = 'lightblue'; } target1.ondrop = target2.ondrop =target3.ondrop =target4.ondrop =function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } this.style.backgroundColor = 'orange'; } </script>
以上所述是小編給大家介紹的JavaScript原生拖放,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
如何利用moment處理時(shí)間戳并計(jì)算時(shí)間的差值
前端很多場景都會(huì)涉及到對時(shí)間的處理,我所用得最多的庫是moment,下面這篇文章主要給大家介紹了關(guān)于如何利用moment處理時(shí)間戳并計(jì)算時(shí)間的差值的相關(guān)資料,需要的朋友可以參考下2022-04-04javascript得到當(dāng)前頁的來路即前一頁地址的方法
這篇文章主要介紹了javascript得到當(dāng)前頁的來路即前一頁地址的方法,需要的朋友可以參考下2014-02-02JavaScript 空位補(bǔ)零實(shí)現(xiàn)代碼
JavaScript代碼實(shí)現(xiàn)空位補(bǔ)零2010-02-02javascript實(shí)現(xiàn)對表格元素進(jìn)行排序操作
這篇文章主要介紹了javascript實(shí)現(xiàn)對表格元素進(jìn)行排序操作,分別可以實(shí)現(xiàn)點(diǎn)擊之后排序和還原,和排升序和降序的功能,文章末尾附完整代碼,感興趣的小伙伴們可以參考一下2015-11-11JS的遞增/遞減運(yùn)算符和帶操作的賦值運(yùn)算符的等價(jià)式
JS的遞增/遞減運(yùn)算符和帶操作的賦值運(yùn)算符的等價(jià)式...2007-12-12bootstrap fileinput插件實(shí)現(xiàn)預(yù)覽上傳照片功能
這篇文章主要介紹了bootstrap fileinput插件實(shí)現(xiàn)預(yù)覽上傳照片功能,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-01-01使用JavaScript判斷圖片是否加載完成的三種實(shí)現(xiàn)方式
有時(shí)需要獲取圖片的尺寸,這需要在圖片加載完成以后才可以,本文有三個(gè)不錯(cuò)的實(shí)現(xiàn)方式在此與大家分享下2014-05-05