반응형
1. structure
<div id="list">
<div class="draggable">A</div>
<div class="draggable">B</div>
<div class="draggable">C</div>
<div class="draggable">D</div>
<div class="draggable">E</div>
</div>
2. css
.opacity { /* */
opacity: 0.5;
}
.drag { /* */
position: fixed;
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.16);
}
.placeholder { /* */
margin-bottom: 10px;
height: 4px;
margin-top: -14px; /* default space + placeholder height */
transform: translateY(7px); /* placeholder margin-top / 2 */
background-color: deeppink;
border-radius: 2px;
}
2. script
document.addEventListener('DOMContentLoaded', function () {
const list = document.getElementById('list');
let dragEle;
let cloneEle;
let placeholder;
let isDraggingStarted = false;
let x = 0;
let y = 0;
const swap = function (nodeA, nodeB) {
const parentA = nodeA.parentNode;
const siblingA = nodeA.nextSibling === nodeB ? nodeA : nodeA.nextSibling;
nodeB.parentNode.insertBefore(nodeA, nodeB);
parentA.insertBefore(nodeB, siblingA);
};
const isAbove = function (nodeA, nodeB) {
const rectA = nodeA.getBoundingClientRect();
const rectB = nodeB.getBoundingClientRect();
return rectA.top + rectA.height / 2 < rectB.top + rectB.height / 2;
};
const mouseDownHandler = function (e) {
if (e.button == 0) {
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
}
};
const mouseMoveHandler = function (e) {
if (!isDraggingStarted) {
dragEle = e.target.closest('.draggable');
cloneEle = dragEle.cloneNode(true);
cloneEle.classList.add('opacity');
dragEle.classList.add('drag');
const draggingRect = dragEle.getBoundingClientRect();
isDraggingStarted = true;
dragEle.insertAdjacentElement('afterend', cloneEle);
const rect = dragEle.getBoundingClientRect();
x = e.pageX - rect.left + window.scrollX;
y = e.pageY - rect.top + window.scrollY;
placeholder = document.createElement('div');
placeholder.classList.add('placeholder');
dragEle.parentNode.insertBefore(placeholder, dragEle.nextSibling);
}
dragEle.style.top = `${e.pageY - y}px`;
dragEle.style.left = `${e.pageX - x}px`;
const prevEle = dragEle.previousElementSibling;
const nextEle = placeholder.nextElementSibling;
if (prevEle && isAbove(dragEle, prevEle)) {
swap(placeholder, dragEle);
swap(placeholder, prevEle);
return;
}
if (nextEle && isAbove(nextEle, dragEle)) {
swap(nextEle, placeholder);
swap(nextEle, dragEle);
}
};
const mouseUpHandler = function () {
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
placeholder && placeholder.parentNode.removeChild(placeholder);
cloneEle && cloneEle.parentNode.removeChild(cloneEle);
dragEle.style.removeProperty('top');
dragEle.style.removeProperty('left');
dragEle.style.removeProperty('position');
dragEle.classList.remove('drag');
x = null;
y = null;
dragEle = null;
cloneEle = null;
isDraggingStarted = false;
};
[].slice.call(list.querySelectorAll('.draggable')).forEach(function (item) {
item.addEventListener('mousedown', mouseDownHandler);
});
});
반응형
댓글