iT邦幫忙

DAY 15
7

HTML5試試看系列 第 15

[HTML5試試看-15] 互動機制 - drag and drop

過去在網頁裡面實現drag and drop是很麻煩的,需要配合CSS、onmousedown、onmouseup、setInterval()等,來做出元件拖曳的視覺效果。HTML5直接為drag and drop設計了完整的drag and drop機制,利用這些機制就可以很簡單地做出拖曳的效果。
HTML5的拖曳,使用幾個新定義的DOM物件屬性及事件協作:

  1. draggable:在標籤設定此元素為draggable時,這個元素就可以被拖曳
  2. dragstart事件:由被拖曳物件觸發,在這個事件可以設定一些拖曳事件的性質與資料
  3. dragenter事件:滑鼠在拖曳狀態下,滑鼠指標進入目標物件時由目標物件觸發
  4. dragover事件:滑鼠在拖曳狀態下,滑鼠指標在目標物件上時由目標物件觸發
  5. dragleave事件:滑鼠在拖曳狀態下,滑鼠指標在離開目標物件時由目標物件觸發
  6. drop事件:滑鼠在拖曳狀態下,在目標物件上放開滑鼠按鈕時,由目標物件觸發
  7. dragend事件:拖曳結束後,由被拖曳物件觸發

另外,拖曳事件有一個特別的物件屬性,叫做DataTransfer。這個物件是拖曳時資料傳送及行為控制的重點。定義如下:

 interface DataTransfer {
            attribute DOMString dropEffect;
            attribute DOMString effectAllowed;
 
   readonly attribute DOMStringList types;
   void clearData(in optional DOMString format);
   void setData(in DOMString format, in DOMString data);
   DOMString getData(in DOMString format);
   readonly attribute FileList files;
 
   void setDragImage(in Element image, in long x, in long y);
   void addElement(in Element element);
 };

簡單的drag and drop操作流程如下:

在dragstart,被拖曳物件可以做幾件事:設定effectAllowed、呼叫setData把要傳遞的資料傳給DataTransfer物件、利用setDragImage來指定圖片以設定拖曳的視覺效果、利用addElement來指定用哪個元素作為拖曳的視覺效果。effectAllowed可以設定為'none', 'copy', 'copyLink', 'copyMove', 'link', 'linkMove', 'move', 'all' 與 'uninitialized'。其中,'copyLink', 'copyMove', 'linkMove'表示同時允許兩種效果,'all'表示允許全部效果。

在dragover時,目標物件必須呼叫Event.preventDefault(),讓事件的預設動作停止執行,這樣drop事件才會觸發。同時,在dragover可以設定DataTransfer.dropEffect。他允許的值有'none', 'copy', 'move', 'link',必須與effectAllowed設定的值匹配,才能觸發drop事件。

在drop時,才能使用getData取得用setData傳過來的資料。在dragstart呼叫DataTransfer.setData(format, data),其中format可以指定mime type。不過經過一些測試,比較沒問題的是'text/plain'。用DataTransfer.getData(format)取得資料時,必須用同樣的format參數來取。另外,可以參考DataTransfer.types,然後使用這些傳進來的type字串,用getData()來取得資料。

以下用一個例子來示範:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
	.container {
		background: #336699;
		width: 120px;
		height: 120px;
		border: solid #6699CC 2px;
		padding: 3px 3px 3px 3px;
		border-radius: 5px;
	}
	.object {
		border: solid #244872 2px;
		border-radius: 3px;
		text-align: center;
		background: #99CCFF;
		padding: 2px 2px 2px 2px;
		cursor: move;
	}
</style>
<script>
function ListIterator(o, cb) {
	for(var i=0; i<o.length; i++) {
		cb(o.item(i));
	}
}
</script>


<div id="panel1" class="container">
<div id="source1" class="object">Test1</div>
<div id="source2" class="object">Test2</div>
<div id="source3" class="object">Test3</div></div>
<br>
<div id="panel2" class="container"></div>


<script>
(function(){
document.ondragover = function(e){e.preventDefault();};
document.ondrop = function(e){e.preventDefault();};
ListIterator(
	document.getElementsByClassName('object'),
	function(o){o.draggable = true;}
);
ListIterator(
	document.getElementsByClassName('object'),
	function(o){
		o.draggable = true;
		o.addEventListener('dragstart', function(e) {
			e.dataTransfer.effectAllowed = 'all';
			e.dataTransfer.setData('text/plain', this.id);
		},false);
	}
);

ListIterator(
	document.getElementsByClassName('container'),
	function(o){
		o.addEventListener('dragenter', function(e){
			e.preventDefault();
		},false);
		o.addEventListener('dragover', function(e){
			e.preventDefault();
			e.dataTransfer.dropEffect = 'copy';
		},false);
		o.addEventListener('drop', function(e){
			e.preventDefault();
			e.stopPropagation();
			if(e.target!==e.currentTarget) return false;
			if(e.dataTransfer.types.length>0) {
				for(var i=0; i<e.dataTransfer.types.length; i++) {
					if(e.dataTransfer.types[i] === 'text/plain') {
						var sourceid = e.dataTransfer.getData('text/plain');
						var source = document.getElementById(sourceid);
						e.currentTarget.appendChild(source.parentNode.removeChild(source));
					}
				}
				return false;
			}
		},false);
	}
);
})();
</script>

這是在Chrome上執行的效果:

明天來進一步討論drag and drop與其他應用軟體互動。

參賽文章


上一篇
[HTML5試試看-14] 表單驗證
下一篇
[HTML5試試看-16] 互動機制 - drag and drop(續)
系列文
HTML5試試看30

尚未有邦友留言

立即登入留言