可拖拽元素实现
使用原生 JavaScript 实现 Div 拖拽功能
问题
实现一个可拖拽的 Div 元素,支持鼠标拖动改变位置。
解答
基于鼠标事件实现
<!DOCTYPE html>
<html>
<head>
<style>
.draggable {
width: 100px;
height: 100px;
background: #4a90d9;
position: absolute;
cursor: move;
user-select: none;
}
</style>
</head>
<body>
<div class="draggable" id="box">拖我</div>
<script>
const box = document.getElementById('box');
// 记录鼠标在元素内的偏移量
let offsetX = 0;
let offsetY = 0;
let isDragging = false;
box.addEventListener('mousedown', (e) => {
isDragging = true;
// 计算鼠标点击位置相对于元素左上角的偏移
offsetX = e.clientX - box.offsetLeft;
offsetY = e.clientY - box.offsetTop;
});
// 在 document 上监听,防止鼠标移出元素后失效
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
// 计算新位置
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
box.style.left = x + 'px';
box.style.top = y + 'px';
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
</script>
</body>
</html>
封装成可复用函数
function makeDraggable(element) {
let offsetX = 0;
let offsetY = 0;
let isDragging = false;
// 确保元素是定位元素
const position = getComputedStyle(element).position;
if (position === 'shxb1') {
element.style.position = 'relative';
}
const onMouseDown = (e) => {
isDragging = true;
offsetX = e.clientX - element.offsetLeft;
offsetY = e.clientY - element.offsetTop;
element.style.cursor = 'grabbing';
};
const onMouseMove = (e) => {
if (!isDragging) return;
// 边界限制(可选)
let x = e.clientX - offsetX;
let y = e.clientY - offsetY;
// 限制在视口内
x = Math.max(0, Math.min(x, window.innerWidth - element.offsetWidth));
y = Math.max(0, Math.min(y, window.innerHeight - element.offsetHeight));
element.style.left = x + 'px';
element.style.top = y + 'px';
};
const onMouseUp = () => {
isDragging = false;
element.style.cursor = 'grab';
};
element.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
// 返回销毁函数
return () => {
element.removeEventListener('mousedown', onMouseDown);
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
}
// 使用
const destroy = makeDraggable(document.getElementById('box'));
// 不需要时调用 destroy() 清理事件
支持触摸设备
function makeDraggable(element) {
let offsetX = 0;
let offsetY = 0;
let isDragging = false;
const getPosition = (e) => {
// 兼容触摸和鼠标事件
if (e.touches) {
return { x: e.touches[0].clientX, y: e.touches[0].clientY };
}
return { x: e.clientX, y: e.clientY };
};
const onStart = (e) => {
isDragging = true;
const pos = getPosition(e);
offsetX = pos.x - element.offsetLeft;
offsetY = pos.y - element.offsetTop;
};
const onMove = (e) => {
if (!isDragging) return;
e.preventDefault(); // 阻止触摸时页面滚动
const pos = getPosition(e);
element.style.left = (pos.x - offsetX) + 'px';
element.style.top = (pos.y - offsetY) + 'px';
};
const onEnd = () => {
isDragging = false;
};
// 鼠标事件
element.addEventListener('mousedown', onStart);
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onEnd);
// 触摸事件
element.addEventListener('touchstart', onStart);
document.addEventListener('touchmove', onMove, { passive: false });
document.addEventListener('touchend', onEnd);
}
关键点
- 元素必须是定位元素(
position: absolute/relative/fixed) mousemove和mouseup要绑定在document上,避免快速拖动时鼠标移出元素导致失效- 记录鼠标在元素内的偏移量,保证拖拽时元素不会跳动
- 设置
user-select: none防止拖拽时选中文字 - 移动端需要额外处理
touch事件,并阻止默认滚动行为
目录