话不多说,直接看效果!
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表拖动</title>
<style>
.item {
cursor: move;
user-select: none;
display: flex;
justify-content: center;
align-items: center;
width: 200px;
height: 60px;
border: 1px solid #cbd5e0;
margin: 10px 0;
box-sizing: border-box;
}
.placeholder {
box-sizing: border-box;
background-color: #edf2f7;
margin: 10px 0;
border: 2px dashed #cbd5e0;
}
</style>
</head>
<body>
<div id="list">
<div class="item">第一个元素</div>
<div class="item">第二个元素</div>
<div class="item">第三个元素</div>
<div class="item">第四个元素</div>
<div class="item">第五个元素</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const list = document.getElementById('list');
list.querySelectorAll('.item').forEach(item => {
item.addEventListener('mousedown', mouseDownHandler);
});
let draggingElement;
let x = 0;
let y = 0;
let placeholder;
let isDraggingStarted = false;
function mouseDownHandler(e) {
draggingElement = e.target;
const rect = draggingElement.getBoundingClientRect();
x = e.clientX - rect.left;
y = e.clientY - rect.top;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
};
function mouseMoveHandler(e) {
const draggingRect = draggingElement.getBoundingClientRect();
if (!isDraggingStarted) {
isDraggingStarted = true;
placeholder = document.createElement('div');
placeholder.classList.add('placeholder');
draggingElement.parentNode.insertBefore(placeholder, draggingElement.nextSibling);
placeholder.style.width = draggingRect.width + 'px';
placeholder.style.height = draggingRect.height + 'px';
}
const left = e.clientX - x;
const top = e.clientY - y;
draggingElement.style.position = 'absolute';
draggingElement.style.top = `${top}px`;
draggingElement.style.left = `${left}px`;
const prevEle = draggingElement.previousElementSibling;
const nextEle = placeholder.nextElementSibling;
if (prevEle && isAbove(draggingElement, prevEle)) {
swap(placeholder, draggingElement);
swap(placeholder, prevEle);
return;
}
if (nextEle && !isAbove(draggingElement, nextEle)) {
swap(nextEle, placeholder);
swap(nextEle, draggingElement);
}
};
function mouseUpHandler() {
placeholder && placeholder.parentNode.removeChild(placeholder);
isDraggingStarted = false;
draggingElement.style.removeProperty('top');
draggingElement.style.removeProperty('left');
draggingElement.style.removeProperty('position');
x = 0;
y = 0;
draggingElement = null;
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
}
function isAbove(nodeA, nodeB) {
const rectA = nodeA.getBoundingClientRect();
const rectB = nodeB.getBoundingClientRect();
const centerPointA = rectA.top + rectA.height / 2;
const centerPointB = rectB.top + rectB.height / 2;
return centerPointA < centerPointB;
};
function swap(nodeA, nodeB) {
const parentA = nodeA.parentNode;
const siblingA = nodeA.nextSibling === nodeB ? nodeA : nodeA.nextSibling;
nodeB.parentNode.insertBefore(nodeA, nodeB);
parentA.insertBefore(nodeB, siblingA);
};
});
</script>
</body>
</html>