🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

The HTML5 Drag and Drop API: A Complete Native Sequence

Master the drag-and-drop event sequence: draggable and dragstart for beginning a drag, the mandatory preventDefault() in dragover for enabling drops, and drop with dataTransfer.getData() for completing the transfer.

Total XP: 0|💻 html XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

Drag and Drop API

Native sequence, end to end.


🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

HTML5's Drag and Drop API provides a complete, native event sequence for implementing draggable interfaces — reorderable lists, file drop zones — without any external library, once its one critical gotcha is understood.

1Making An Element Draggable And Starting The Sequence

Setting draggable="true" on an element (note: many elements like images and links are draggable by default already; this attribute is most relevant for arbitrary elements like <div>) opts it into the browser's native drag behavior. The dragstart event fires the instant the user begins dragging, and is the standard place to call event.dataTransfer.setData(format, value), attaching whatever data identifies what's being dragged — an item's ID, a file reference, arbitrary text.

<div draggable="true" id="item-1">Drag me</div>
item.addEventListener('dragstart', (e) => {
  e.dataTransfer.setData('text/plain', 'item-1');
});
localhost:3000
✓ Drag Sequence InitiatedData identifying the dragged item is attached at the very start of the sequence.

2The Mandatory preventDefault() In dragover

This is the single most common implementation mistake with the Drag and Drop API: by default, browsers disallow dropping on virtually all elements. A drop zone must explicitly listen for the dragover event (which fires continuously as a dragged item hovers over it) and call event.preventDefault() inside that handler — this call is the required signal overriding the browser's default 'no drop allowed here' behavior.

Without this call, the subsequent drop event simply never fires on that element at all, regardless of how correctly the rest of the implementation is written — a frequent, confusing source of 'drag and drop just doesn't work' bug reports.

dropZone.addEventListener('dragover', (e) => {
  e.preventDefault(); // REQUIRED — without this, drop never fires
});
localhost:3000
⚠ The Single Most Common GotchaWithout preventDefault() here, the drop event will never fire on this element.

3Completing The Transfer With drop

When the user releases the drag over a correctly-configured drop target, the drop event fires. Inside its handler, event.dataTransfer.getData(format) retrieves whatever value was set via setData() back in dragstart, threading the identifying data cleanly through the entire sequence without relying on any external, global state.

Calling event.preventDefault() in the drop handler as well is also standard practice, preventing the browser's default handling of the dropped data (like navigating to a dropped link's URL) from interfering with the custom application logic.

dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  const itemId = e.dataTransfer.getData('text/plain');
  moveItemTo(itemId, dropZone);
});
localhost:3000
Sequence:
dragstart (set data) → dragover (allow) → drop (retrieve)

4Step-by-Step Breakdown

Native Drag And Drop, No Library Required. Before reaching for a drag-and-drop library, it's worth knowing HTML5 already ships a complete native Drag and Drop API — a sequence of events and a draggable attribute that together implement reorderable lists, file drop zones, and more.

draggable Enables Dragging, dragstart Begins The Sequence. Setting draggable="true" on an element makes it draggable by the user; the dragstart event fires the moment dragging begins, the standard place to set what data is being dragged via the event's dataTransfer object.

Starting A Drag Operation. What attribute makes an element draggable, and what event fires when dragging begins?

  • draggable="true" attribute, dragstart event
  • movable attribute, dragbegin event
  • No attribute needed; all elements are draggable by default

dragover Must Call preventDefault() To Allow Dropping. By default, browsers disallow dropping on most elements — a drop zone's dragover handler must explicitly call event.preventDefault() to signal 'this element accepts drops', or the subsequent drop event will never fire at all.

The dragover Requirement. What happens if a drop zone's dragover handler doesn't call preventDefault()?

  • Dropping still works normally regardless
  • The browser's default behavior disallows the drop, and the drop event never fires
  • It throws a JavaScript error immediately

drop Retrieves The Transferred Data. The drop event fires when the user releases the drag over a valid drop target, and event.dataTransfer.getData() retrieves whatever data was set during dragstart, completing the full drag-and-drop data transfer cycle.

Completing The Drag And Drop Cycle. How does the drop event handler access the data that was set in the dragstart handler?

  • It must be stored in a global JavaScript variable
  • Via event.dataTransfer.getData(), reading the value set during dragstart
  • It's automatically written to a data attribute on the drop target

Drag And Drop API Mastered. You now know how to start a drag with draggable/dragstart, the critical mandatory preventDefault() call in dragover that enables dropping, and how drop retrieves the transferred data via dataTransfer — a complete native drag-and-drop implementation with zero external libraries.

Make An Element Draggable. The native Drag and Drop API requires draggable="true" on the source element.

Level Up 🚀

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Native Drag And Drop Is Not Inherently Keyboard-Accessible And Needs A Supplementary Interaction Path

Mouse-driven drag interactions exclude keyboard-only and many switch-device users entirely — a fully accessible implementation needs an alternative mechanism, like buttons to move items up/down, alongside drag and drop.

SEO Implications

  • 1

    Native Drag And Drop Avoids The JavaScript Bundle Weight Of A Third-Party Library

    For straightforward reordering or file-drop use cases, the native API avoids shipping and maintaining an external dependency, indirectly supporting smaller bundle size and better performance metrics.

Best Practices

Always Call preventDefault() In The dragover Handler For Any Valid Drop Target

It's the single required step that's easy to forget and the most common cause of a drag-and-drop implementation silently failing to accept drops.

Provide A Non-Drag-Based Alternative Interaction For Reordering Or Moving Content

Since native drag and drop excludes keyboard-only users, an accessible implementation needs a supplementary mechanism like explicit move-up/move-down buttons.

Frequent Bugs

THE BUG

A carefully implemented drop zone never actually receives a drop event, no matter how the drag is performed.

THE FIX

Verify event.preventDefault() is called inside the dragover event handler — its absence is the most common cause of this exact symptom.

THE BUG

Dropping a dragged link or image navigates the browser to that URL instead of triggering custom application logic.

THE FIX

Call event.preventDefault() in the drop handler as well, to override the browser's default drop-handling behavior.

Real-World Examples

A Complete Reorderable List Implementation

A simple drag-to-reorder list using the full native event sequence.

items.forEach(item => {
  item.draggable = true;
  item.addEventListener('dragstart', e => e.dataTransfer.setData('text/plain', item.id));
});
list.addEventListener('dragover', e => e.preventDefault());
list.addEventListener('drop', e => {
  e.preventDefault();
  const id = e.dataTransfer.getData('text/plain');
  reorderList(id, e.target);
});

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Forgetting preventDefault() in the dragover handler

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

The Solution //

Always call preventDefault() in dragover for any element meant to accept drops.

The Error //

Providing no non-drag interaction alternative

<!-- Drag and drop should have a keyboard-accessible fallback -->

The Solution //

Add an accessible alternative like move-up/move-down buttons for keyboard-only users.

Lesson Glossary

[01]draggable

The attribute enabling native drag behavior on an element.

Code Preview
draggable="true"

[02]dataTransfer

The object carrying data through the drag sequence.

Code Preview
e.dataTransfer

[03]dragover preventDefault()

The mandatory call enabling a valid drop target.

Code Preview
Required, easy to forget

[04]drop Event

Fires when the drag is released over a valid target.

Code Preview
getData() retrieves the payload

Continue Learning