Reordering a list by dragging

draggable and the drag events

Baseline widely available
  • Chrome3
  • Edge12
  • Firefox3.5
  • Safari4

Features it needs

Sortable.js tracks pointer position, computes where a dragged item would land, and reorders the DOM to match, all in its own event handling. The native drag-and-drop API does the same core job: mark an element draggable, listen for dragstart, dragover, and drop, and move it yourself in the drop handler. It gives you the events and the ghost image; the reordering logic is still yours to write.

When this applies

Letting someone reorder a list by dragging an item with a mouse.

The native approach

<!-- Without draggable, dragstart never fires. -->
<li draggable="true" data-id="a1">Item</li>

<script>
  el.addEventListener("dragstart", (e) => {
    e.dataTransfer.setData("text/plain", el.dataset.id);
  });

  // Without preventDefault here, the element is not a drop target and the
  // drop event below never fires at all. This is the step people miss.
  list.addEventListener("dragover", (e) => {
    e.preventDefault();
  });

  list.addEventListener("drop", (e) => {
    e.preventDefault();
    const id = e.dataTransfer.getData("text/plain");
    // move the item with this id to the drop position
  });
</script>

MDN reference

When the dependency is still right

An answer that always says "the platform covers it" is worse than no answer. These are the cases where this one does not hold.

  • You need this to work with touch. The native drag events have no built-in touch support, so a touch-specific fallback is still required.
  • You need a drop-placeholder animation or an auto-scrolling container while dragging near an edge. The native API gives you the events; the animation and scroll logic are still yours to build.
  • You want the dataTransfer API's plain-text-and-files model to also carry rich in-memory objects between drag and drop without round-tripping through a serialized string.
  • You need accessible, keyboard-operable reordering. The native drag events are pointer-only; keyboard support is separate work either way.

Packages this covers