js实现拖动元素
实现元素拖动的基本原理
通过监听鼠标事件(mousedown、mousemove、mouseup)实现元素拖动。核心逻辑是计算鼠标移动距离并更新元素位置。
基本实现代码
const draggableElement = document.getElementById('draggable');
let isDragging = false;
let offsetX, offsetY;
draggableElement.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - draggableElement.getBoundingClientRect().left;
offsetY = e.clientY - draggableElement.getBoundingClientRect().top;
draggableElement.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
draggableElement.style.left = `${e.clientX - offsetX}px`;
draggableElement.style.top = `${e.clientY - offsetY}px`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
draggableElement.style.cursor = 'grab';
});
CSS 初始设置
确保元素可定位并设置初始样式:
#draggable {
position: absolute;
cursor: grab;
user-select: none; /* 防止拖动时选中文本 */
}
优化版本(边界限制)
防止元素被拖出视窗:
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
// 视窗边界检测
const maxX = window.innerWidth - draggableElement.offsetWidth;
const maxY = window.innerHeight - draggableElement.offsetHeight;
draggableElement.style.left = `${Math.max(0, Math.min(x, maxX))}px`;
draggableElement.style.top = `${Math.max(0, Math.min(y, maxY))}px`;
});
使用事件委托实现多元素拖动
document.addEventListener('mousedown', (e) => {
if (!e.target.classList.contains('draggable')) return;
const element = e.target;
const rect = element.getBoundingClientRect();
const offsetX = e.clientX - rect.left;
const offsetY = e.clientY - rect.top;
function moveHandler(e) {
element.style.left = `${e.clientX - offsetX}px`;
element.style.top = `${e.clientY - offsetY}px`;
}
function upHandler() {
document.removeEventListener('mousemove', moveHandler);
document.removeEventListener('mouseup', upHandler);
}
document.addEventListener('mousemove', moveHandler);
document.addEventListener('mouseup', upHandler);
});
使用 HTML5 Drag API 实现
更简单的原生拖动方案:
document.querySelectorAll('.draggable').forEach(el => {
el.draggable = true;
el.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', e.target.id);
e.target.classList.add('dragging');
});
});
document.addEventListener('dragover', (e) => {
e.preventDefault();
});
document.addEventListener('drop', (e) => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
const draggable = document.getElementById(id);
draggable.style.left = `${e.clientX}px`;
draggable.style.top = `${e.clientY}px`;
draggable.classList.remove('dragging');
});
性能优化建议
对于频繁触发的 mousemove 事件,可以使用节流(throttling)技术:
function throttle(fn, delay) {
let lastCall = 0;
return function(...args) {
const now = new Date().getTime();
if (now - lastCall < delay) return;
lastCall = now;
return fn(...args);
};
}
document.addEventListener('mousemove', throttle((e) => {
// 拖动逻辑
}, 16)); // 约60fps






