JS简单拖放程序
在大学做web应用时,就搞过拖放,当时从网上搜索的相关程序,感觉代码很复杂,最终效果还不好,自己也没调通,所以在脑子里总感觉拖放是很复杂的东西。最近正好有时间,再写下这种程序。
要实现拖放,基本思路就是:鼠标点下时监听移动,抬起时停止监听。
var x,y;
function move(e) {
el.style.left = e.clientX - x + 'px';
el.style.top = e.clientY - y + 'px';
}
function stop() {
E.detach(document, 'mousemove', move);
E.detach(document, 'mouseup', stop);
}
E.on(el, 'mousedown', function(e) {
x = e.clientX - el.offsetLeft;
y = e.clientY - el.offsetTop;
E.on(document, 'mousemove', move);
E.on(document, 'mouseup', stop);
});
这样拖放就实现了,当然还有一些问题:
拖放禁止
1.当第一次拖放后,再次拖放元素就会发现会出现拖放禁止图标,是因为第一次拖放时点击拖放元素,拖放元素被选择,第二次拖放时使用了浏览器默认拖放。浏览器对于 image,link,selection默认是可以拖放的。
2.拖放过程中移动到其他元素时,其他元素被选择,再次拖放也会出现拖放禁止图标,原因同上。
解决这个问题:
方法一:preventDefault
问题1,在mousedown中阻止默认动作,防止元素被选择;问题2,在mousemove中阻止默认动作,防止其他元素被选择(opera貌似无效)。
这个方法有点体验不足:在ie9,ff等现代浏览器将 “清除之前选择”的默认动作也阻止了,如果之前有选择内容,拖放时原选择不会被清除。
方法二:清空 selection
mousedown,mousemove的时候清空一下selection即可解决上面两个问题
有的程序也通过setCapture来阻止默认事件,并且扩大捕获,兼容性不好不考虑;
综合考虑,采用清空selection比较好。
异常终止问题
1.窗口失去焦点时(如:alt+tab切换,alert等),无法触发mouseup(chrome 可以触发),导致拖放不终止,回到窗口时继续拖放,体验不好
2.页面中存在iframe时,鼠标移动到iframe中事件传递到iframe的document中,无法到达原来的document导致无法触发mouseup,mousemove等
解决第一个问题,可以监听下window blur事件,终止拖放;但这里有个问题:ie通过document.selection.empty()方法清除selection,当selection有内容时,导致window触发blur事件,拖放终止。这个触发是在mousedown处理完之后触发的,即window blur注册之后再触发,解决可以考虑setTimeout延时注册下。
解决第二个问题,考虑将事件仍传递到本页面document,可以建个遮罩层将iframe遮挡(为了简洁,这里不实现)。
这样简单的拖放程序就出来了,demo,code:
function Drag(el) {
this.el = el;
this._initEvents();
}
Drag.prototype = {
moveTo : function(x, y) {
var el = this.el;
el.style.left = x + 'px';
el.style.top = y + 'px';
},
_initEvents : function() {
var x,y,self = this,el = this.el;
function move(e) {
// 防止移动过程中选择其他元素,保证体验一致
util.clearSelection();
self.moveTo(e.clientX - x, e.clientY - y);
}
function stop() {
E.detach(document, 'mousemove', move);
E.detach(document, 'mouseup', stop);
E.detach(window, 'blur', stop);
}
E.on(this.el, 'mousedown', function(e) {
// 清除之前选择,防止拖拽时禁止
util.clearSelection();
var style = util.getComputeStyle(el);
x = e.clientX - el.offsetLeft + (parseInt(style.marginLeft) || 0);
y = e.clientY - el.offsetTop + (parseInt(style.marginTop) || 0);
E.on(document, 'mousemove', move);
E.on(document, 'mouseup', stop);
// 失去焦点时终止 document.selection.empty() 会触发blur,延时注册
setTimeout(function(){E.on(window, 'blur', stop);},0);
});
}
};
如果要做成组件,当然还需要考虑全面一些,如:iframe情况,移动范围,非绝对定位元素,移动元素和控制元素不同,相关事件支持等等。现在回头看看实现不复杂,只是遇到问题时自己跑偏了,之前看过《JavaScript 拖放效果》这篇文章,觉得很多问题需要处理,然后写的时候遇到问题就去看文中的介绍,而并没真正从问题本身和解决方法上入手,结果严重影响了效率,而且弄的晕头转向,不知在为了解决什么问题而加代码。有参考固然是好的,但是需要更多的独立思考,理清思路,关注本质,这样程序才能得心应手,才是自己的程序。