JSFiddle - React, Tailwind, and code Playground
by Ben Schnell
HTML
<ul class="container" data-alignment="horizontal">
<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: 600px;
display: flex;
flex-wrap: wrap;
}
.container li {
height: 30px;
background-color: #fafafa;
border: 1px solid #888;
display: flex;
align-items: center;
margin: 5px;
padding: 0 15px 0 0;
box-sizing: border-box;
}
.dragging {
opacity: 0.5;
}
.container li span {
background-color: #000;
margin-right: 10px;
color: #fff;
padding: 0 10px;
min-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", (e) => {
draggable.classList.remove("dragging");
dragUpdateNr();
});
});
containers.forEach((container) => {
container.addEventListener("dragover", dragOver);
});
function dragOver(e) {
e.preventDefault();
const afterElement = getDragAfterElement(this, e);
const draggable = document.querySelector(".dragging");
if (afterElement == null) {
this.appendChild(draggable);
} else {
this.insertBefore(draggable, afterElement);
}
}
function getDragAfterElement(container, event) {
const draggableElements = [
...container.querySelectorAll(".draggable:not(.dragging)"),
];
const elementsWithinRow = draggableElements.filter((element) => {
const elementBounds = element.getBoundingClientRect();
const y = event.clientY;
return elementBounds.top < y && y < elementBounds.bottom ? true : false;
});
// If drop is on left side of element, return element itself
// If drop is on right side of element, return next element
const elementBeingDroppedOn = elementsWithinRow.find((element) => {
const elementBounds = element.getBoundingClientRect();
const x = event.clientX;
return elementBounds.left < x && x < elementBounds.right ? true : false;
});
const elementCenter =
(elementBeingDroppedOn.getBoundingClientRect().left +
elementBeingDroppedOn.getBoundingClientRect().right) /
2;
if (event.clientX < elementCenter) return elementBeingDroppedOn;
if (event.clientX > elementCenter)
return elementBeingDroppedOn.nextElementSibling;
}
function dragUpdateNr() {
let uls = document.querySelectorAll("ul.container");
uls.forEach((ul) => {
let lis = ul.querySelectorAll("li.draggable");
for (let i...