js实现拖拽
实现拖拽的基本步骤
使用JavaScript实现拖拽功能需要监听鼠标事件,包括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设置了position: absolute或position: relative,否则left和top属性不会生效:

#draggable {
position: absolute;
cursor: grab;
user-select: none; /* 防止拖动时选中文本 */
}
处理边界限制
为防止元素被拖出视口,可以添加边界检查逻辑:
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const newX = e.clientX - offsetX;
const newY = e.clientY - offsetY;
const maxX = window.innerWidth - draggableElement.offsetWidth;
const maxY = window.innerHeight - draggableElement.offsetHeight;
draggableElement.style.left = `${Math.max(0, Math.min(newX, maxX))}px`;
draggableElement.style.top = `${Math.max(0, Math.min(newY, maxY))}px`;
});
使用HTML5拖拽API
HTML5原生支持拖拽功能,通过draggable属性和拖拽事件实现:

<div id="draggable" draggable="true">拖拽我</div>
<script>
const draggable = document.getElementById('draggable');
draggable.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', draggable.id);
});
document.addEventListener('dragover', (e) => {
e.preventDefault(); // 允许放置
});
document.addEventListener('drop', (e) => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
const element = document.getElementById(id);
element.style.left = `${e.clientX}px`;
element.style.top = `${e.clientY}px`;
});
</script>
拖拽排序的实现
如果需要实现列表拖拽排序,可以结合拖拽事件和DOM操作:
const list = document.getElementById('sortable-list');
let draggedItem = null;
list.addEventListener('dragstart', (e) => {
draggedItem = e.target;
e.dataTransfer.effectAllowed = 'move';
});
list.addEventListener('dragover', (e) => {
e.preventDefault();
const target = e.target.closest('li');
if (target && target !== draggedItem) {
const rect = target.getBoundingClientRect();
const next = (e.clientY - rect.top) / (rect.bottom - rect.top) > 0.5;
list.insertBefore(draggedItem, next ? target.nextSibling : target);
}
});
list.addEventListener('dragend', () => {
draggedItem = null;
});
性能优化建议
对于频繁触发的mousemove事件,可以使用节流(throttle)减少计算压力:
function throttle(fn, delay) {
let lastCall = 0;
return function(...args) {
const now = new Date().getTime();
if (now - lastCall < delay) return;
lastCall = now;
fn.apply(this, args);
};
}
document.addEventListener('mousemove', throttle((e) => {
if (!isDragging) return;
// 拖拽逻辑
}, 16)); // 约60fps






