浏览器鼠标事件原生api实现拖拽功能
涉及技术点
一、Mouse Api
1、mouseenter
:鼠标移入事件;当鼠标移入到指定元素上时触发;从子元素的移入不会触发该事件;不支持事件冒泡
2、mouseleave
:鼠标离开事件;当鼠标指针移出指定元素时就会触发;从当前元素移入到子元素不会触发该事件;不支持事件冒泡
3、mouseover
:鼠标移入事件;当鼠标移入到指定元素上时触发;从子元素的移入会触发该事件
4、mouseout
:鼠标离开事件;当鼠标指针移出指定元素时就会触发;从当前元素移入到子元素会触发该事件
5、mousedown
:鼠标按键按下时会触发该事件;在鼠标按键按下时会触发(左键、右键和滚轮按下后均会触发)
6、mouseup
:鼠标按键弹起事件;在鼠标按键抬起时会触发(左键、右键和滚轮抬起后均会触发)
7、mousemove
:鼠标移动事件;鼠标在指定元素对象上移动时就会触发
二、DOM元素数据
1、offsetTop、offsetLeft
:表示该元素的左上角与父容器(offsetParent对象)左上角的距离。
2、clientWidth、clientHeight
:表示元素的内容部分再加上padding的所占据的视觉面积,不包括border和滚动条占用的空间。
3、offsetWidth、offsetHeight
:表示元素的内容部分再加上padding、border的所占据的视觉面积,不包括滚动条占用的空间。
案例实现
1.第一步:准备DOM元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浏览器鼠标事件原生api实现拖拽功能</title>
<style type="text/css">
* {
padding: 0;
margin: 0;
}
body {
box-sizing: border-box;
padding: 50px;
width: 100vw;
height: 100vh;
}
#parent {
box-sizing: border-box;
position: relative;
height: 300px;
background: #ededed;
}
#dragItem {
display: inline-block;
padding: 10px 15px;
background: #2f54eb;
color: #fff;
font-size: 14px;
cursor: pointer;
}
</style>
</head>
<body>
<!-- 父容器 -->
<div id="parent">
<!-- 可拖拽子项 -->
<div id="dragItem">我是可拖拽子项</div>
</div>
</body>
</html>
2.第二步:初始化数据及添加监听事件
// 获取要添加监听事件的DOM元素
var parent = document.getElementById("parent")
var dragItem = document.getElementById("dragItem")
// 声明过程中使用到的变量
// dragingState:鼠标是否处于拖拽状态
// fromX:鼠标开始拖拽时,记录鼠标的x位置
// fromY:鼠标开始拖拽时,记录鼠标的y位置
// fromOffsetTop:鼠标开始拖拽时,记录拖拽元素距离父元素的顶部距离
// fromOffsetLeft:鼠标开始拖拽时,记录拖拽元素距离父元素的左部距离
var dragingState,fromX,fromY,fromOffsetTop,fromOffsetLeft
// 当页面加载完成,对相应DOM元素添加监听事件
window.onload = function () {
document.body.addEventListener('mousemove', listenMouseMove)
parent.addEventListener('mouseenter', listenParentMouseEnter)
parent.addEventListener('mouseleave', listenParentMouseLeave)
dragItem.addEventListener('mousedown', listenMouseDown)
dragItem.addEventListener('mouseup', listenMouseUp)
}
3.第三步:添加监听鼠标移动事件处理
// 监听鼠标移动事件
function listenMouseMove(e) {
// 当鼠标不处于拖拽状态时,直接返回,不往下继续执行
if (!dragingState) return
const { x, y } = e
// 如果鼠标位置在父容器外部则触发释放资源
if (x < parent.offsetLeft
|| x > parent.offsetLeft + parent.offsetWidth
|| y < parent.offsetTop
|| y > parent.offsetTop + parent.offsetHeight) {
// 创建一个鼠标mouseup事件
const eventObj = document.createEvent('MouseEvents')
eventObj.initMouseEvent('mouseup')
dragItem.dispatchEvent(eventObj)
return
}
// 正常移动
const moveX = x - fromX
const moveY = y - fromY
parent.style.border = '1px dashed #ccc'
let changeX = fromOffsetLeft + moveX
let changeY = fromOffsetTop + moveY
// 当移动距离会让子元素位于父元素之外时,处理最大最小数据
if (changeX < 0) {
changeX = 0
} else if (changeX > parent.clientWidth - dragItem.clientWidth) {
changeX = parent.clientWidth - dragItem.clientWidth
}
if (changeY < 0) {
changeY = 0
} else if (changeY > parent.clientHeight - dragItem.clientHeight) {
changeY = parent.clientHeight - dragItem.clientHeight
}
// 执行移动
dragItem.style.position = 'absolute'
dragItem.style.left = `${changeX}px`
dragItem.style.top = `${changeY}px`
}
- 第四步:添加鼠标按下事件处理
// 在子元素上监听鼠标按下事件
function listenMouseDown(e) {
// 修改拖拽状态为拖拽中
dragingState = true
const { x, y } = e
// 记录鼠标位置
fromX = x
fromY = y
// 记录元素位置
fromOffsetTop = dragItem.offsetTop
fromOffsetLeft = dragItem.offsetLeft
}
5.第五布:添加鼠标弹起事件处理
// 在子元素上监听鼠标弹起事件
function listenMouseUp() {
// 修改拖拽状态为拖拽停止
dragingState = false
// 还原父元素状态
parent.style.border = '1px solid transparent'
}
6.第六步:对鼠标移入移出父元素做样式凸显
// 当鼠标移入父元素时,对父元素做样式凸显
function listenParentMouseEnter(index) {
parent.style.boxShadow = '0px 0px 8px #8c8c8c'
}
// 当鼠标移出父元素时,对父元素做样式重置
function listenParentMouseLeave(index) {
parent.style.boxShadow = 'none'
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。