JSFiddle - React, Tailwind, and code Playground
by Ben Schnell
HTML
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Colorado</li>
<li class="draggable" draggable="true"><span>1</span> Montana</li>
<li class="draggable" draggable="true"><span>2</span> Utah</li>
<li class="draggable" draggable="true"><span>3</span> Montana</li>
<li class="draggable" draggable="true"><span>4</span> Wyoming</li>
</ul>
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Maine</li>
<li class="draggable" draggable="true"><span>1</span> North Carolina</li>
<li class="draggable" draggable="true"><span>2</span> Florida</li>
<li class="draggable" draggable="true"><span>3</span> Virginia</li>
<li class="draggable" draggable="true"><span>4</span> New York</li>
</ul>
CSS
.container {
max-width: 300px;
}
.container li {
height: 30px;
background-color: #fafafa;
border: 1px solid #888;
display: flex;
align-items: center;
}
.dragging {
opacity: 0.5;
}
.container li span {
background-color: #000;
margin-right: 10px;
color: #fff;
padding: 0 10px;
height: 100%;
display: flex;
align-items: center;
}
JavaScript
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
containers.forEach(container => {
container.addEventListener('dragover', dragOver);
})
function dragOver(e) {
e.preventDefault()
const afterElement = getDragAfterElement(this, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
this.appendChild(draggable)
} else {
this.insertBefore(draggable, afterElement)
}
}
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child }
} else {
return closest
}
}, { offset: Number.NEGATIVE_INFINITY }).element
}