目录
1.准备
- 准备一个正方形盒子作为拖拽的对象,简单的给些样式以及设置绝对定位
- 清除浏览器默认样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
/* 清除浏览器默认的边距 */
margin: 0;
padding: 0;
}
#box {
width: 100px;
height: 100px;
position: absolute;
background-color: red;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
2.效果实现
需要用到的方法、属性
- 需要用到的鼠标事件有:
- mousedown/ mouseup 鼠标按下/松开时触发
- mouseover 当指针设备 ( 通常指鼠标 ) 在元素上移动时,mousemove 事件被触发。
- 在window.onload事件中添加逻辑
load 事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。
- clientX,clientY
- 用于获取鼠标相对于可视窗口的偏移量
- offsetLeft,offsetTop
offsetLeft:当前元素相对于其定位元素的水平偏移量
offsetTop:当前元素相对于其定位元素的垂直偏移量
拖拽过程
- 在我们点击盒子的时候开始拖拽,接着盒子跟着鼠标进行移动
- 鼠标松开将盒子位置固定住
移动盒子
- 在鼠标按下时执行移动逻辑,获取盒子dom元素,绑定
onmousedown,onmouseup
事件对于鼠标按下,抬起进行监听;- 给页面(document)绑定onmousemove事件,即
document.onmousemove
,鼠标在移动时clientX,clientY也随之变化,将最终偏移量设置给盒子的left, top属性;- 盒子位置设置完成后,在鼠标抬起时解绑事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
/* 清除浏览器默认的边距 */
margin: 0;
padding: 0;
}
#box {
width: 100px;
height: 100px;
position: absolute;
background-color: red;
}
</style>
</head>
<body>
<script type="text/javascript">
window.onload = function() {
const box = document.getElementById('box')
// 鼠标按下
box.onmousedown = function(event) {
event = event || window.event
console.log('鼠标按下', event)
// 鼠标在页面移动时触发,设置盒子位置
document.onmousemove = function(e) {
// console.log('进入了', e)
e = e || window.event
// 获取鼠标坐标
const left = e.clientX + 'px'
const top = e.clientY + 'px'
box.style.left = left
box.style.top = top
}
// 鼠标松开
box.onmouseup = function(event) {
console.log('松开了', event)
// 取消跟随鼠标移动,鼠标松开事件
document.onmousemove = null
document.onmouseup = null
}
}
}
</script>
</body>
3.出现的问题
问题1(重点)
- 很快我们能发现在移动过程中,无论点击盒子的那个位置,最终鼠标的光标总是指向盒子的左上角;
- 而上面的代码确实也是存在些漏洞,首先我们已经知道了clientX,clientY是鼠标相对于可视窗口的偏移量,这个点位就是我们希望盒子移动时的点位,而不是总指向左上角;但是我们在给盒子设置位置时,是直接将clientX,clientY直接设置给了left与top;
- 画了一个简图(橙色盒子为我们初始位置移动后的过程):
-
- 可以看出给盒子设置的距离应该是太大了,因为我们希望
clientX与clientY
设置给盒子距离左边和顶部的距离就是包括鼠标中心点左边以及上边的距离; - 那么发现问题之后,我们要做的就是给盒子设置的left与top需要减去多出的偏移量;而这多出的距离,就是中心点距离盒子左边和上边的距离;
- 这时候之前介绍的offsetLeft,offsetTop派上用场,它们能够获取到当前元素相对于其 offsetParent (这里指的就是document)元素的左边和顶部的距离;结合已有的clientX,Y偏移量,相减得到咱们多出的距离
- 可以看出给盒子设置的距离应该是太大了,因为我们希望
最终我们修改设置盒子位置的逻辑
document.onmousemove = function(e) {
e = e || window.event //兼容写法
const ol = e.clientX - box.offsetLeft
const ot = e.clientY - box.offsetTop
// 获取鼠标坐标
const left = e.clientX - ol + 'px'
const top = e.clientY - ot + 'px'
box.style.left = left
box.style.top = top
}
问题2
- 当我们拖动时,一直往同一个方向盒子有可能会超出可视区域
- 结果就像这样:
- 问题就在于没有给盒子设置边界,这里通过left,top值的大小设置左边与顶部的拖拽范围;
- 如果还想对右边以及底部做出限制,那么可以获取父容器(这里指document)的宽高,属性是cilentWidth,clientHeight,这里我就写个左边和顶部为例了
添加左,上边界
// 鼠标在页面移动时触发,设置盒子位置
document.onmousemove = function(e) {
e = e || window.event //兼容写法
// 左边
let left = e.clientX - ol < 0 ? 0 : e.clientX - ol
// 顶部
let top = e.clientY - ot < 0 ? 0 : e.clientY - ot
box.style.left = left + 'px'
box.style.top = top + 'px'
}
问题3
- 当页面上存在两个盒子的时候,移动的盒子经过另一个盒子时并且出现重叠的情况下,在鼠标松开后无法将盒子定位至所在区域
- 就像下图这样:
- 问题就出在重叠时,onmouseup事件变为了绿盒子(box1)的,所以没有触发红盒子(box)的鼠标抬起事件;
- 为了测试我们给绿盒子(box1)添加onmouseup事件,然后在红盒子(box)移入其中并且松开鼠标时看控制台输出情况;
解决方式
- 这里有两个方式,一是通过css样式给盒子添加层级,因为这两个盒子都是绝对定位,在移动过程中因为相互覆盖导致的问题,这里通过
z-index
属性把红盒子层级设置高一点或者绿盒子设置低一点,保证红盒子重叠时处于上方
#box {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
z-index: 99;
}
- 二是把鼠标松开事件绑定给盒子的父元素也就是document,这里采用这种方式进行处理
// 鼠标松开
document.onmouseup = function(event) {
console.log('box红,鼠标松开了')
// 取消跟随鼠标移动,鼠标松开事件
document.onmousemove = null
document.onmouseup = null
}
4.最终版本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
/* 清除浏览器默认的边距 */
margin: 0;
padding: 0;
}
#box {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
/* z-index: 99; */
}
// #box1 {
// width: 100px;
// height: 100px;
// left: 600px;
// top: 300px;
// background-color: green;
// position: absolute;
// }
</style>
</head>
<body>
<div id="box"></div>
<div id="box1"></div>
<script type="text/javascript">
window.onload = function() {
const box = document.getElementById('box')
// const box1 = document.getElementById('box1')
// box1.onmouseup = function() {
// console.log('box1鼠标松开了')
// }
// 鼠标按下
box.onmousedown = function(event) {
// 设置box捕获所有鼠标点击事件 仅支持ie
box.setCapture && box.setCapture()
event = event || window.event
const ol = event.clientX - box.offsetLeft
const ot = event.clientY - box.offsetTop
// 鼠标在页面移动时触发,设置盒子位置
document.onmousemove = function(e) {
e = e || window.event //兼容写法
// 获取鼠标坐标
let left = e.clientX - ol < 0 ? 0 : e.clientX - ol
let top = e.clientY - ot < 0 ? 0 : e.clientY - ot
box.style.left = left + 'px'
box.style.top = top + 'px'
}
// 鼠标松开
document.onmouseup = function(event) {
// console.log('box红鼠标松开了')
// 取消跟随鼠标移动,鼠标松开事件
document.onmousemove = null
document.onmouseup = null
// 取消捕获所有鼠标点击事件
box.releaseCapture && box.releaseCapture()
}
/*
1.当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,
此时会导致拖拽功能的异常,这个是浏览器提供的默认行为,
2. 如果不希望发生这个行为,则可以通过return false来取消默认行为
3. 但是这招对工E8不起作用
*/
return false
}
}
</script>
</body>
</html>
5.写在最后
- 以上就是js实现拖拽的全部内容了,如果觉得文章写得不够好或是存在问题欢迎评论指正,感谢你这么优秀还来看我的文章。