Preview:
//HTML
  <div class="container">
      <ul class="list">
        <li class="list--item" draggable="true">1</li>
        <li class="list--item" draggable="true">2</li>
        <li class="list--item" draggable="true">3</li>
        <li class="list--item" draggable="true">4</li>
      </ul>
    </div>

//CSS
* {
  padding: 0;
  box-sizing: border-box;
}

body {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.container {
  width: min(90%, 500px);
  background-color: #333;
}

.list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  list-style: none;
  padding: 1rem;
}

.list--item {
  padding: 1rem;
  background-color: #efefef;
}

.dragging {
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  transition: 0.3s;
}

//JS
const list = document.querySelector('.list');
const items = document.querySelectorAll('.list--item');

const getDragAfterElement = (y) => {
  //Selects all the elements in the container that are not being dragged
  const draggableElements = [
    ...list.querySelectorAll('.list--item:not(.dragging)'),
  ];

  //Loop through the elements' list and determine which single element is right after our mouse cursor based on the y position that we pass in
  return draggableElements.reduce(
    (closest, child) => {
      const box = child.getBoundingClientRect();
      //Get half of the box on the y axis
      const offset = y - box.top - box.height / 2;
      //First the number needs to be negative, that is how we know that we are hovering over another child element, but we need the closest one, so the one which offset is the smallest
      if (offset < 0 && offset > closest.offset) {
        return { offset: offset, element: child };
      } else {
        return closest;
      }
    },
    { offset: Number.NEGATIVE_INFINITY }
  ).element;
};

items.forEach((item) => {
  item.addEventListener('dragstart', () => {
    item.classList.add('dragging');
  });
  item.addEventListener('dragend', () => {
    item.classList.remove('dragging');
  });
});

list.addEventListener('dragover', (e) => {
  e.preventDefault();

  const draggable = document.querySelector('.dragging');

  const afterElement = getDragAfterElement(e.clientY);
  if (afterElement == null) {
    //going to be added as the last child if no element is after
    list.appendChild(draggable);
  } else {
    //going to be added as before the next element
    list.insertBefore(draggable, afterElement);
  }
});
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter