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.
Fundamentals
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.
Concepts
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.
Source
Creating Draggable Elements
Set draggable="true" on any HTML element you want the user to drag:
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.
Events
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.');
});
Target
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;
});
UX
Customizing Drag and Drop
Improve the experience with visual feedback and custom drag images:
Custom drag image — event.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.
Caution
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.
Flow
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.
Cheat Sheet
⚡ Quick Reference
Event
Target
Typical action
dragstart
Source
setData('text/plain', value)
dragover
Drop zone
event.preventDefault()
dragleave
Drop zone
Remove hover class
drop
Drop zone
getData('text/plain')
dragend
Source
Reset styles
Enable drag
Source
draggable="true"
Hands-On
Examples Gallery
Six examples from a draggable box to a full demo. Each includes View Output and Try It Yourself.
Combines every step: draggable source, setData, dragover with preventDefault, and drop handler.
Pro Tips
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
Applications
🚀 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.
Compatibility
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 ChromeAll versions · Desktop & Mobile
Full support
Mozilla FirefoxAll versions · Desktop & Mobile
Full support
Apple SafariAll versions · macOS & iOS
Full support
Microsoft EdgeAll versions · Chromium & Legacy
Full support
Internet ExplorerIE 6+ · Legacy environments
Full support
OperaAll 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.
Wrap Up
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.
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.