Mastering HTML5 Drag-and-Drop: Native Implementation, Events, and React Alternatives

This article explains how to implement native HTML5 drag‑and‑drop, details the required draggable attribute, describes all related events, shows a simple JavaScript demo, discusses common pitfalls such as repeated ondragover and ghosting, and compares popular React libraries for drag‑and‑drop.

ELab Team
ELab Team
ELab Team
Mastering HTML5 Drag-and-Drop: Native Implementation, Events, and React Alternatives

Final Result

Native Implementation Principle

About Dragging

Image tags are draggable by default; other elements (e.g., div) need the attribute draggable="true" to become draggable.

Drag‑and‑Drop Events

On the drag source

ondragstart – triggered when the user starts dragging the element.

ondrag – triggered while the element is being dragged.

ondragend – triggered after the drag operation completes.

On the drop target

ondragenter – fires when the dragged object enters a container.

ondragover – fires continuously while the dragged object is over another container.

ondragleave – fires when the dragged object leaves a container.

ondrop – fires when the mouse button is released during a drag.

Note: ondragend and ondragover have default behaviors that must be prevented; otherwise the element may leave a ghost image.

element.ondragover = event => {
  event.preventDefault();
  // ...
};

A Simple Native JS Drag Demo

Thinking Process

ondragstart

Obtain the source element.

ondrag

Change the source element's style during dragging.

ondragend

Remove extra styles, retrieve the target index from ondragover, and update the list array.

ondragover

Generate a short‑lived snapshot of the drag path to determine the final target and add movement animations to intermediate elements.

Problem Solving

How to Transfer Data

Method 1: set className="index${index}" and parse the index from event.target.classList – not elegant.

Method 2: set a data-index attribute and read it via event.target.dataset.index – chosen for its semantic clarity.

Nested DOM Causing Repeated ondragover

When the source element has multiple DOM layers, each layer triggers ondragover, leading to data confusion.

Solution: use e.currentTarget instead of e.target to identify the container.

Drag End Ghosting

After dropping, the element may drift back to its original position before moving to the target.

Solution: prevent the default behavior on the parent element's ondragover.

onDragOver={(e) => {
  e.preventDefault();
}};

Suitable Drag Transition Animation

Use CSS animations to create an upward or downward “making space” effect.

.drag-up {
  animation: dragup ease 0.1s 1;
  animation-fill-mode: forwards;
}
.drag-down {
  animation: dragdown ease 0.1s 1;
  animation-fill-mode: forwards;
}
@keyframes dragup {
  from { margin-top: 10px; }
  to   { margin-top: 50px; }
}
@keyframes dragdown {
  from { margin-bottom: 10px; margin-top: 50px; }
  to   { margin-bottom: 50px; margin-top: 10px; }
}

Other Third‑Party Libraries

react-dnd – larger bundle, TypeScript support, good maintenance, uses HTML5 drag API, repository: https://github.com/react-dnd/react-dnd

react-beautiful-dnd – >100 KB, no TypeScript support, good maintenance, also based on HTML5 drag API, repository: https://github.com/atlassian/react-beautiful-dnd

react-sortable-hoc – smaller bundle, no TypeScript support, unmaintained for over a year, uses HTML5 mouse API, repository: https://github.com/clauderic/react-sortable-hoc/issues

For simple requirements, native implementation remains valuable because the two larger libraries are not lightweight.

Core Code

const [dragged, setDragged] = useState<any>();
const [over, setOver] = useState<any>();
const [draggable, setDraggable] = useState(false);

const dragStart = (e: any) => {
  e.currentTarget.style.backgroundColor = "#fafafa";
  setDragged(e.currentTarget);
};

const dragEnd = (e: any) => {
  e.preventDefault();
  e.target.style.display = "flex";
  e.target.classList.remove("drag-up", "drag-down");
  over.classList.remove("drag-up", "drag-down");
  const from = cloneDeep(value[dragged.dataset.index]);
  const to = cloneDeep(value[over.dataset.index]);
  splice(dragged.dataset.index, 1, to);
  splice(over.dataset.index, 1, from);
  e.target.style.opacity = "1";
  e.target.style.backgroundColor = "";
};

const dragOver = (e: any) => {
  e.preventDefault();
  const dgIndex = dragged.dataset.index;
  const taIndex = e.currentTarget.dataset.index;
  const animateName = dgIndex > taIndex ? "drag-up" : "drag-down";
  if (over && e.currentTarget.dataset.index !== over.dataset.index) {
    over.classList.remove("drag-up", "drag-down");
  }
  if (!e.currentTarget.classList.contains(animateName)) {
    e.currentTarget.classList.add(animateName);
    setOver(e.currentTarget);
  }
};
{items.map((item, index) => (
  return (
    <Item
      draggable={draggable}
      data-index={index}
      onDragStart={e => { dragStart(e); }}
      onDrag={e => { e.preventDefault(); e.target.style.opacity = '0'; }}
      onDragOver={dragOver}
      onDragEnd={e => { dragEnd(e); }}
    >
      // your list data
    </Item>
  )
)})

References

ondragstart – https://www.runoob.com/jsref/event-ondragstart.html

ondrag – https://www.runoob.com/jsref/event-ondrag.html

ondragend – https://www.runoob.com/jsref/event-ondragend.html

ondragover – https://www.runoob.com/jsref/event-ondragover.html

ondragleave – https://www.runoob.com/jsref/event-ondragleave.html

ondrop – https://www.runoob.com/jsref/event-ondrop.html

HTML5 Drag and Drop API – https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

nativeJavaScriptReActHTML5Drag and Drop
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.