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...