HTML Drag and Drop

Beginner
⏱️ 14 min read
📚 Updated: Jul 2026
🎯 6 Examples + 6 Try It
DnD API

Introduction

The HTML Drag and Drop API lets users drag elements within a page or transfer data between areas. It powers intuitive interactions like rearranging lists, kanban boards, and file upload zones.

This tutorial covers draggable elements, drag events, drop zones, data transfer, customization, common pitfalls, and a complete working example you can run in Try It.

What You’ll Learn

01

draggable

Enable drag.

02

Events

dragstart.

03

Drop zone

dragover.

04

Data

dataTransfer.

05

Style

Visual cues.

06

Demo

Full page.

What Is Drag and Drop?

Drag and Drop is a user interface interaction where users click an element, move it while holding the mouse button, and release it over a target. The browser fires a sequence of events you handle with JavaScript to move DOM nodes, copy data, or trigger actions.

Unlike CSS-only hover effects, drag and drop requires explicit setup: a draggable source, event listeners, and a drop target that accepts the release.

Basic Concepts

  • Draggable — elements the user can pick up and move (draggable="true").
  • Drag events — fired on the source: dragstart, drag, dragend.
  • Drop zones — target areas that accept a dropped element.
  • Drop events — fired on the target: dragenter, dragover, dragleave, drop.
  • dataTransfer — object for passing string data between source and target during a drag.
💡
Beginner Tip

The draggable attribute is documented in the draggable attribute reference. This tutorial focuses on the full event-driven API built on top of it.

Creating Draggable Elements

Set draggable="true" on any HTML element you want the user to drag:

html
<div id="draggable" draggable="true">Drag me!</div>

In dragstart, store data the drop zone will read later:

js
const draggable = document.getElementById('draggable');

draggable.addEventListener('dragstart', function (event) {
  event.dataTransfer.setData('text/plain', 'This is the dragged data');
});

Images and anchor links are draggable by default; most other elements need the attribute explicitly.

Handling Drag Events

Key events on the dragged element:

  • dragstart — user begins dragging; call setData here.
  • drag — fires repeatedly while dragging (use sparingly—can hurt performance).
  • dragend — drag finished (dropped or cancelled); reset styles here.
js
draggable.addEventListener('dragstart', function (event) {
  event.dataTransfer.setData('text/plain', 'Dragged Data');
});

draggable.addEventListener('dragend', function () {
  console.log('Drag operation ended.');
});

Dropping Elements

Create a drop zone in HTML:

html
<div id="dropzone">Drop here</div>

Handle drop target events in JavaScript:

  • dragover — must call event.preventDefault() to allow dropping.
  • dragleave — pointer left the zone; remove hover styles.
  • drop — user released; read getData and update the UI.
js
const dropzone = document.getElementById('dropzone');

dropzone.addEventListener('dragover', function (event) {
  event.preventDefault(); // Required — allows the drop
});

dropzone.addEventListener('drop', function (event) {
  event.preventDefault();
  const data = event.dataTransfer.getData('text/plain');
  dropzone.textContent = 'Dropped data: ' + data;
});

Customizing Drag and Drop

Improve the experience with visual feedback and custom drag images:

  • Custom drag imageevent.dataTransfer.setDragImage(img, x, y) in dragstart.
  • Opacity while dragging — add a CSS class in dragstart, remove in dragend.
  • Highlight drop zone — toggle a class on dragover / dragleave.
  • Cursor — use cursor: grab and cursor: grabbing in CSS.
js
draggable.addEventListener('dragstart', function (event) {
  const img = new Image();
  img.src = 'custom-drag-image.png';
  event.dataTransfer.setDragImage(img, 0, 0);
});

Use same-origin images for custom drag images to avoid cross-origin restrictions.

Common Pitfalls

  • Missing preventDefault() on dragover — drops silently fail without it.
  • Forgetting setData — Firefox will not start a drag unless you call setData in dragstart (even for style-only demos).
  • Cross-origin drag images — custom images may not render if loaded from another domain.
  • Touch devices — native DnD is mouse-oriented; test mobile separately.
  • Accessibility — provide keyboard alternatives; drag-only UIs exclude many users.

How Drag and Drop Works

1

dragstart

User grabs the element. You call setData on dataTransfer.

2

dragover

Pointer moves over drop zone. Call preventDefault each time.

3

drop

User releases. Read getData and update DOM or state.

4

dragend

Cleanup on source—reset opacity, classes, or placeholders.

⚡ Quick Reference

EventTargetTypical action
dragstartSourcesetData('text/plain', value)
dragoverDrop zoneevent.preventDefault()
dragleaveDrop zoneRemove hover class
dropDrop zonegetData('text/plain')
dragendSourceReset styles
Enable dragSourcedraggable="true"

Examples Gallery

Six examples from a draggable box to a full demo. Each includes View Output and Try It Yourself.

📚 Getting Started

Enable dragging and pass data.

Example 1 — Draggable Element

html
<div draggable="true" style="padding:16px;background:#3b82f6;color:#fff;border-radius:8px;cursor:grab;display:inline-block">
  Drag me!
</div>
Try It Yourself

How It Works

draggable="true" tells the browser the element can be picked up. Without JavaScript, it can still drag but won’t drop meaningfully.

Example 2 — dragstart with setData

js
draggable.addEventListener('dragstart', function (e) {
  e.dataTransfer.setData('text/plain', 'Hello from dragstart!');
});
Try It Yourself

How It Works

Data set in dragstart is retrieved in drop with the same MIME type string.

📦 Drop Zones

Accept drops and show feedback.

Example 3 — Basic Drop Zone

js
dropzone.addEventListener('dragover', function (e) { e.preventDefault(); });
dropzone.addEventListener('drop', function (e) {
  e.preventDefault();
  dropzone.textContent = 'Dropped: ' + e.dataTransfer.getData('text/plain');
});
Try It Yourself

How It Works

preventDefault on both dragover and drop is required for a successful drop.

Example 4 — dragstart and dragend

js
el.addEventListener('dragstart', function (e) {
  e.dataTransfer.setData('text/plain', 'dragging'); // required in Firefox
  el.classList.add('dragging'); // opacity: 0.5
});
el.addEventListener('dragend', function () {
  el.classList.remove('dragging');
});
Try It Yourself

How It Works

dragend runs whether the drop succeeded or was cancelled—always reset visual state here.

Example 5 — Highlight Drop Zone on dragover

js
dropzone.addEventListener('dragover', function (e) {
  e.preventDefault();
  dropzone.classList.add('over');
});
dropzone.addEventListener('dragleave', function () {
  dropzone.classList.remove('over');
});
Try It Yourself

How It Works

Visual feedback on dragover tells users where they can release the item.

Example 6 — Complete Example

Full page with styled draggable box and drop zone:

html
<div id="draggable" draggable="true">Drag me!</div>
<div id="dropzone">Drop here</div>
<script>
  draggable.addEventListener('dragstart', function (e) {
    e.dataTransfer.setData('text/plain', 'Dragged Data');
  });
  dropzone.addEventListener('dragover', function (e) { e.preventDefault(); });
  dropzone.addEventListener('drop', function (e) {
    e.preventDefault();
    dropzone.textContent = 'Dropped data: ' + e.dataTransfer.getData('text/plain');
  });
</script>
Try It Yourself

How It Works

Combines every step: draggable source, setData, dragover with preventDefault, and drop handler.

Best Practices

✅ Do

  • Call preventDefault() on every dragover
  • Set data in dragstart before drop reads it
  • Add visual feedback (opacity, borders) during drag
  • Reset styles in dragend
  • Provide keyboard-accessible alternatives where possible

❌ Don’t

  • Assume drops work without dragover prevention
  • Run heavy logic inside the drag event (fires often)
  • Rely on DnD alone for mobile touch interactions
  • Use drag-only UI for critical actions
  • Forget to test in multiple browsers

🚀 Common Use Cases

  • Kanban boards — move task cards between columns.
  • File upload zones — drop files from the desktop (uses File API with DnD).
  • Sortable lists — reorder navigation or playlist items.
  • Layout builders — drag widgets onto a canvas.
  • Shopping carts — drag products into a basket area.
  • Image galleries — rearrange thumbnails before saving.

Universal Browser Support

The HTML5 Drag and Drop API is supported in all modern desktop browsers (Chrome, Firefox, Safari, Edge). Touch support is limited; test mobile behavior separately or use a touch-friendly library.

Baseline · Since HTML

Drag and Drop API (desktop)

The HTML5 Drag and Drop API is supported in all modern desktop browsers (Chrome, Firefox, Safari, Edge). Touch support is limited; test mobile behavior separately or use a touch-friendly library.

96% Modern browser support
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
Drag and Drop API (desktop) Excellent

Bottom line: Safe for desktop interactive UIs. Plan an alternative interaction path for touch-only users.

Conclusion

The HTML Drag and Drop API provides a flexible way to build interactive web applications. By setting draggable="true", handling drag events, and configuring drop zones with preventDefault, you can create intuitive drag-and-drop experiences.

Next, explore the Geolocation API for location-aware apps, or read the draggable attribute reference for attribute details.

Key Takeaways

📦 02

setData

dragstart.

Data
👇 03

dragover

preventDefault.

Drop
🎨 04

Feedback

Visual cues.

UX
05

a11y

Keyboard too.

Access

❓ Frequently Asked Questions

It is a browser API that lets users click an element, drag it, and drop it onto a target zone. You set draggable="true" on the source, listen for drag events, and handle drop on the target with JavaScript.
Add draggable="true" to the element: <div draggable="true">Drag me</div>. By default only images and links are draggable; other elements need this attribute explicitly.
The most common cause is forgetting event.preventDefault() in the dragover handler. Browsers block drops unless you prevent the default on dragover. Also ensure dragstart calls setData if you need to read data in drop.
event.dataTransfer is available during drag events. Use setData(type, value) in dragstart and getData(type) in drop to pass strings between source and target. Types are usually 'text/plain' or 'text/html'.
dragover fires continuously while the dragged item is over a drop zone—use it to call preventDefault and show hover styles. drop fires once when the user releases the mouse over a valid target—use it to read data and update the UI.
Native HTML5 drag and drop has limited touch support. Desktop browsers work well. For mobile-first apps, consider pointer events or libraries like SortableJS that add touch-friendly drag behavior.
Did you know?

HTML5 Drag and Drop is separate from the CSS cursor: grab styling and from touch “drag” gestures on phones. Desktop browsers implement the full event model; many mobile browsers do not fire the same events for finger drags. Libraries like SortableJS bridge that gap for sortable lists.

Build drag-and-drop in the editor

Open Try It, drag the blue box onto the green zone, and watch the drop handler update the page.

Open Try It editor →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful