[1]拖放源 [2]拖放目標 [3]dataTransfer對象 [4]改變游標 ...
×
目錄
[1]拖放源 [2]拖放目標 [3]dataTransfer對象[4]改變游標前面的話
拖放(drag-and-drop,DnD)其實是兩個動作——拖和放。所以,它涉及到兩個元素。一個是被拖的元素,稱為拖放源;另一個是要放的目標,稱為拖放目標。本文將通過拆分這兩個概念來詳細介紹原生拖放
拖放源
什麼樣的元素才是拖放源呢?
HTML5為所有HTML元素規定了一個draggable屬性,表示元素是否可以拖動
圖像和鏈接的draggable屬性自動被設置成了true,而其他元素這個屬性的預設值都是false
[註意]必須設置draggable='true'才能生效,只設置draggable不起作用
預設情況下,文本只有在被選中的情況下才能拖動,而圖像和鏈接在任何時候都可以拖動。而其他元素則無法被拖放
<input value="文字可拖動"> <img alt="圖像可拖動" src="http://files.cnblogs.com/files/xiaohuochai/zan.gif"> <a href="#">鏈接可拖動</a> <div id="test" style="height:30px;width:300px;background:pink;">元素不可拖動</div>
當為元素設置draggable屬性後,普通元素也可以拖動
<div draggable="true" style="height:30px;width:100px;background:pink;"></div>
相容
IE9-瀏覽器不支持draggable屬性,但可通過mousedown事件處理程式調用dragDrop()方法來實現拖動效果
<div id="test" style="height:30px;width:300px;background:pink;"></div> <script> test.onmousedown = function(){ this.dragDrop(); } </script>
[註意]如果讓firefox支持draggable屬性,必須添加一個ondragstart事件處理程式,併在dataTransfer對象使用setData()方法來啟動效果
拖放事件
拖放源涉及到3個拖放事件。拖動拖放源時,依次觸發dragstart、drag和dragend這3個事件
dragstart
按下滑鼠鍵並開始移動滑鼠時,會在被拖放的元素上觸發dragstart事件。此時游標變成“不能放”符號(圓環中有一條反斜線),表示不能把元素放到自己上面
drag
觸發dragstart事件後,隨即會觸發drag事件,而且在元素被拖動期間會持續觸發該事件
dragend
當拖動停止時(無論是把元素放到了有效的放置目標,還是放到了無效的放置目標上),會觸發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>
拖放目標
拖放目標是指被拖動的元素鬆開滑鼠時被放置的目標
拖放源被拖動到拖放目標上時,將依次觸發dragenter、dragover和dragleave或drop這四個事件
dragenter
只要有元素被拖動到放置目標上,觸發dragenter事件
dragover
被拖動的元素在放置目標的範圍內移動時,持續觸發dragover事件
dragleave
如果元素被拖出了放置目標,觸發dragleave事件
drop
如果元素被放到了放置目標中,觸發drop事件
[註意]firefox瀏覽器的drop事件的預設行為是打開被放到放置目標上的URL。為了讓firefox支持正常的拖放,還要取消drop事件的預設行為
預設情況下,目標元素是不允許被放置的,所以不會發生drop事件。只要在dragover和dragenter事件中阻止預設行為,才能成為被允許的放置目標,才能允許發生drop事件。此時,游標變成了允許放置的符號
<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;">拖放目標</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 = '開始拖動'; } test.ondrag = function(){ if(timer) return; timer = setInterval(function(){ test.innerHTML = '元素已被拖動' + ++i + '秒'; },1000); } test.ondragend = function(){ clearInterval(timer); timer = 0;i =0; this.innerHTML = '結束拖動'; this.style.backgroundColor = 'pink'; } target.ondragenter = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } this.innerHTML = '有元素進入目標區域'; 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 = '元素已進入' + (++i1) + '秒'; },1000); } target.ondragleave = function(){ clearInterval(timer1); timer1 = 0;i1=0; this.innerHTML = '元素已離開目標區域'; this.style.backgroundColor = 'lightblue'; } target.ondrop = function(){ clearInterval(timer1); timer1 = 0;i1=0; this.innerHTML = '元素已落在目標區域'; this.style.backgroundColor = 'orange'; } </script>
dataTransfer對象
為了在拖放操作時實現數據交換,引入了dataTransfer對象,它是事件對象的一個屬性,用於從被拖動元素向放置目標傳遞字元串格式的數據
dataTransfer對象有兩個主要方法:getData()和setData()
getData()可以取得由setData()保存的值。setData()方法的第一個參數,也是getData()方法唯一的一個參數,是一個字元串,表示保存的數據類型,取值為"text"或"URL"
IE只定義了"text"和"URL"兩種有效的數據類型,而HTML5則對此加以擴展,允許指定各種MIME類型。考慮到向後相容,HTML5也支持"text"和"URL",但這兩種類型會被映射為"text/plain"和"text/uri-list"
實際上,dataTransfer對象可以為每種MIME類型都保存一個值。換句話說,同時在這個對象中保存一段文本和一個URL不會有任何問題
[註意]保存在dataTransfer對象中的數據只能在drop事件處理程式中讀取
在拖動文本框中的文本時,瀏覽器會調用setData()方法,將拖動的文本以"text"格式保存在dataTransfer對象中。類似地,在拖放鏈接或圖像時,會調用setData()方法並保存URL。然後,在這些元素被拖放到放置目標時,就可以通過getData()讀到這些數據
<div>請將從這堆內容不同亂七八糟的文字中挑選一些移動到拖放目標中</div> <div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目標</div> <div id="result"></div> <script> target.ondragenter = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } this.innerHTML = '有元素進入目標區域'; 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 = '元素已離開目標區域'; this.style.backgroundColor = 'lightblue'; } target.ondrop = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } result.innerHTML = '落入目標區域的文字為:' + e.dataTransfer.getData('text'); this.innerHTML = '元素已落在目標區域'; this.style.backgroundColor = 'orange'; } </script>
當然,也可以在dragstart事件處理程式中調用setData(),手動保存自己要傳輸的數據,以便將來使用
<div id="test" draggable="true" data-value="這是一個秘密" style="height:30px;width:100px;background:pink;">拖動源</div> <div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目標</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 = '有元素進入目標區域'; 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 = '元素已離開目標區域'; this.style.backgroundColor = 'lightblue'; } target.ondrop = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } result.innerHTML = '落入目標區域的文字為:' + e.dataTransfer.getData('text'); this.innerHTML = '元素已落在目標區域'; this.style.backgroundColor = 'orange'; } </script>
改變游標
利用dataTransfer對象,不僅可以傳輸數據,還能通過它來確定被拖動的元素以及作為放罝目標的元素能夠接收什麼操作。為此,需要訪問dataTransfer對象的兩個屬性:dropEffect和effectAllowed
實際上,這兩個屬性並沒有什麼用,只是拖動源在拖動目標上移動時,改變不同的游標而已(但是,有一種情況除外)
dropEffect
dropEffect屬性可以知道被拖動的元素能夠執行哪種放置行為。這個屬性有下列4個可能的值
"none":不能把拖動的元素放在這裡。這是除文本框之外所有元素的預設值(此時,將無法觸發drop事件)
"move":應該把拖動的元素移動到放置目標
"copy":應該把拖動的元素複製到放置目標
"link":表示放置目標會打開拖動的元素(但拖動的元素必須是一個鏈接,有URL)
在把元素拖動到放置目標上時,以上每一個值都會導致游標顯示為不同的符號
[註意]必須在ondragover事件處理程式中針對放置目標來設置dropEffect屬性
effectAllowed
dropEffect屬性只有搭配effectAllowed屬性才有用。effectAllowed屬性表示允許拖動元素的哪種dropEffect
effectAllowed屬性可能的值如下
"uninitialized":沒有給被拖動的元素設置任何放置行為
"none":被拖動的元素不能有任何行為
"copy":只允許值為"copy"的dropEffect
"link"只允許值為"link"的dropEffect
"move":只允許值為"move"的dropEffect
"copyLink":允許值為"copy"和"link"的dropEffect
"copyMove":允許值為"copy"和"move"的dropEffect
"linkMove":允許值為"link"和"move"的dropEffect
"all":允許任意dropEffect
[註意]必須在ondragstart事件處理程式中設置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)拖放目標</div> <div id="target2" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(move)拖放目標</div> <div id="target3" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(copy)拖放目標</div> <div id="target4" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(link)拖放目標</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