先看看实现效果图, 模拟拖拽最终效果和在桌面上移动文件夹的效果类似
原理介绍
鼠标按下时,拖拽开始。鼠标移动时,被拖拽元素跟着鼠标一起移动。鼠标抬起时,拖拽结束
所以,拖拽的重点是确定被拖拽元素是如何移动的
假设,鼠标按下时,鼠标对象的clientX和clientY分别为x1和x2。元素距离视口左上角x轴和y轴分别为x0和y0
鼠标移动的某一时刻,clientX和clientY分别为x2和y2
所以,元素移动的x轴和y轴距离分别为x2-x1和y2-y1
元素移动后,元素距离视口左上角x轴和y轴的位置分别为
X = x0 + (x2-x1) Y = y0 + (y2-y1)
代码实现
将上面的原理用代码实现如下
鼠标按下时,初始态的x0和y0分别用offsetLeft
和offsetTop
表示
鼠标移动时,瞬时态的x和y分别赋值为定位后元素的left和top
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;"></div> <script> test.onmousedown = function(e){ e = e || event; //获取元素距离定位父级的x轴及y轴距离 var x0 = this.offsetLeft; var y0 = this.offsetTop; //获取此时鼠标距离视口左上角的x轴及y轴距离 var x1 = e.clientX; var y1 = e.clientY; test.onmousemove = function(e){ e = e || event; //获取此时鼠标距离视口左上角的x轴及y轴距离 x2 = e.clientX; y2 = e.clientY; //计算此时元素应该距离视口左上角的x轴及y轴距离 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //将X和Y的值赋给left和top,使元素移动到相应位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } test.onmouseup = function(e){ //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可 test.onmousemove = null; } } </script>
代码优化
使用上面的代码时,会出现一个问题。当鼠标拖动的太快,比onmousemove
事件的触发间隔还要快时,鼠标就会从元素上离开。这样就停止了元素的拖拽过程
此时,如果把mousemove
和mouseup
事件都加在document
上时,即可解决
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;"></div> <script> test.onmousedown = function(e){ e = e || event; //获取元素距离定位父级的x轴及y轴距离 var x0 = this.offsetLeft; var y0 = this.offsetTop; //获取此时鼠标距离视口左上角的x轴及y轴距离 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //获取此时鼠标距离视口左上角的x轴及y轴距离 x2 = e.clientX; y2 = e.clientY; //计算此时元素应该距离视口左上角的x轴及y轴距离 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //将X和Y的值赋给left和top,使元素移动到相应位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } document.onmouseup = function(e){ //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可 document.onmousemove = null; } } </script>
拖拽冲突
由于文字和图片默认支持原生拖放,如果将原生拖放和模拟拖拽掺杂在一起,将造成与预想效果不符的情况
如果拖放的元素内容存在文字,且文字被选中会触发文字的原生拖放效果
在文字上面双击鼠标,即可选中文字,再移动鼠标时,会触发文字的原生拖放效果,如下所示
只要在onmousedown
事件阻止浏览器的默认行为即可
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">测试文字</div> <script> test.onmousedown = function(e){ e = e || event; //获取元素距离定位父级的x轴及y轴距离 var x0 = this.offsetLeft; var y0 = this.offsetTop; //获取此时鼠标距离视口左上角的x轴及y轴距离 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //获取此时鼠标距离视口左上角的x轴及y轴距离 x2 = e.clientX; y2 = e.clientY; //计算此时元素应该距离视口左上角的x轴及y轴距离 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //将X和Y的值赋给left和top,使元素移动到相应位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } document.onmouseup = function(e){ //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可 document.onmousemove = null; } //阻止默认行为 return false; } </script>
IE兼容
以上代码在IE8-浏览器中仍然无法阻止默认行为。此时,为了实现IE兼容,需要使用全局捕获setCapture()
和释放捕获releaseCapture()
首先,先看一下全局捕获的效果
下面代码中,开启全局捕获之后,页面中的所有点击效果,都相当于针对按钮一的点击效果。释放捕获后,效果消失
[注意]IE浏览器完全支持全局捕获;chrome不支持,使用全局捕获会报错;firefox不报错,但静默失败
<button id="btn1">按钮一</button> <button id="btn2">开启按钮一的全局捕获</button> <script> btn1.onclick = function(){ alert(1); } btn2.onclick = function(){ if(btn1.setCapture){ if(btn2.innerHTML.charAt(0) == '开'){ btn1.setCapture(); btn2.innerHTML = '关闭按钮一的全局捕获'; }else{ btn1.releaseCapture(); btn2.innerHTML = '开启按钮一的全局捕获'; } } } </script>
通过在IE浏览器设置全局捕获来达到取消文字原生拖放的默认行为
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">测试文字</div> <script> test.onmousedown = function(e){ e = e || event; //获取元素距离定位父级的x轴及y轴距离 var x0 = this.offsetLeft; var y0 = this.offsetTop; //获取此时鼠标距离视口左上角的x轴及y轴距离 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //获取此时鼠标距离视口左上角的x轴及y轴距离 x2 = e.clientX; y2 = e.clientY; //计算此时元素应该距离视口左上角的x轴及y轴距离 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //将X和Y的值赋给left和top,使元素移动到相应位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } document.onmouseup = function(e){ //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可 document.onmousemove = null; //释放全局捕获 if(test.releaseCapture){ test.releaseCapture(); } } //阻止默认行为 return false; //IE8-浏览器阻止默认行为 if(test.setCapture){ test.setCapture(); } } </script>
总结
以上就是Javascript模拟拖拽的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。