DragDrop 看似简单,实现起来却不大容易。 HTML5 已经提供了用于支持 DragDrop 的事件。使用这些事件可以方便实现 dragdrop。介于有些朋友对 HTML5 的 dragdrop 不熟,在这里先介绍下标准的 dragdrop 。
dragdrop 不是新物,早在 IE4 就有了。但那时只是 IE 的独家技能,所以标准浏览器不支持此事件,现在 HTML5 采用了 IE 的方法,故目前最新版浏览器都支持 DragDrop 。
DragDrop 是 Drag (拖动) Drop(放下) 两个操作。
为实现 Drag ,需要知道以下的事件:
- dragstart - 拖动开始事件
- drag - 拖动时,这个事件不断出现
- dragend - 拖动结束事件
为实现 Drop,需要知道以下的事件:
- dragenter - 拖到当前区事件
- dragover - 拖到当前区时,不断出现
- dragleave - 拖出事件
- drop - 在当前区停下事件
假如拖动一个元素到另一个元素, 发生事件的顺序为:
dragstart - drag (不断) - [ dragenter - dragover/drag (交替) - dragleave/drop - ] dragend
拖动事件的参数 e, 有个成员 dataTransfer , 表示当前拖动的数据。
默认下, img 和 a 标签可拖到。如果需要其它节点也支持拖动,必须指明 draggable="true" 。
根据上文,你肯能会写这样的代码:
<div id="et">拖动委托层</div> <div id="drag" draggable="true"> 被拖动 </div> <div class="asd" id="zone"> 拖动区域
</div> <script> $('drag').on('dragstart', function(e){ console.log(e.type); }).on('drag', function(e){ console.log(e.type); }).on('dragend', function(e){ console.log(e.type); }); </script>
但这个代码只能在 Chrome 正常执行。
对于 IE, 则需要先选择字,然后拖。
对于 Safari, 需加如下代码:
<style> [draggable = true] { -khtml-user-drag: element; } </style>
对于 Firefox, 除了加上面的代码,还需加
function dragstart(e){ e.dataTransfer.setData('Text', this.id); // 火狐必须加这句 }
目前,各浏览器对 DragDrop 的支持不同。老浏览器也不支持 DragDrop 。
所以,必须找一个和谐的办法。
我的目标是模拟浏览器的 dragdrop, 让它和浏览器原生的一样。
先调用一个函数:
$('d1').setDraggable(true); // 设置可拖动
然后这个对象便能拖动,如果还要设置事件,可以:
$('drag').on('dragstart', function(e){ console.log(e.type); }).on('drag', function(e){ console.log(e.type); }).on('dragend', function(e){ console.log(e.type); });
对于目标元素,同样可设置事件,处理拖到当前元素时的处理
$('zone1').on('dragenter', function(e){ trace("dragenter"); return false; }).on('dragover', function(e){ trace("dragover"); return false; }).on('dragleave', function(e){ trace("dragleave"); return false; }).on('drop', function(e){ trace("drop"); });
下面介绍如何实现:
对于 Drag ,需要用 mouse 事件模拟。
先写3个函数:分别处理 mousedown, mousemove, mouseup , 这个相对简单,不多废话。
function mousedown(e ){ // 左键才继续 if(e.which != 1) return; // 初始化位置。 // 暂时保存导致事件发生的目标。 Drag.target = this; // 手动触发 dragstart this.trigger('dragstart', e); document.on('mouseup', mouseup).on('mousemove', mousemove); } function mouseup(e ){ // 左键才继续 if(e.which != 1) return; // 手动触发 dragend this.trigger('dragend', e); // 通知已发生 Drop 事件。 Drag.onDrop(e); document.un('mousemove', mousemove).un('mouseup', mouseup); } function mousemove(e ) // 手动触发 drag this.trigger('drag', e); // 移动目标。 }
然后是实现 Drop,一个 element 拖动时如何正确判断当前到上面元素,并触发那个元素的 drop 事件?
方案甲:
判断目标区域 Zone 是否发生 mouseenter 事件, 在 mouseenter 事件处理是否在拖动,如果同时发生 mouseenter 和 drag ,则 发生 dragenter 。
方案乙:
在拖动时不断计算目标位置的坐标 (Bound) 和 当前元素,如果进入则发生 dragenter 。
因此,先写一个函数判断是否在目标内:
function isEnter(bound, box){ return ((bound.right < box.right && bound.right > box.left) || (bound.left < box.right && bound.left > box.left)) && ((bound.bottom < box.bottom && bound.bottom > box.top) || (bound.top < box.bottom && bound.top > box.top)) ; }
然后在 drag 中,检查当前的 bound 和目标是否符合要求,如果符合要求,继续判断:
function raiseEvent(e){ if(isEnter(bound, box)){ // 如果上次的区域和当前区域一样,表示在同区发生拖动, 应发生 dragover if( Drap.zone == zone) { zone.trigger('dragover', e); } else { // 如果上次在一个zone,现在没有,表示离开一个 zone if(Drap.zone) Drap.zone.trigger('dragleave', e); Drap.zone = zone; // 保存当前的 zone zone.trigger('dragenter', e); return; } } // 现在没有激活的区,但上次有,说明已经拖到上次的外面 if(Drap.zone){ Drap.zone.trigger('dragleave', e); Drap.zone = null; } }
这样稍微加工,就可以完成 DragDrop 。
具体源码在以后一起发。
XULD QQ 744257564