HTML Drag and Drop

Beginner
14 min read
Updated: Nov 2026
6 examples
draggable / DataTransfer

Definition and Usage

The HTML Drag and Drop API lets users select a draggable element with the mouse, drag it, and drop it on a droppable element. A translucent ghost of the item follows the pointer. You customize what can be dragged, what data is transferred, and which HTML elements accept drops.

What are the three parts of a drag?

Every operation involves (1) the item being dragged, (2) the data in the drag data store, and (3) the drop target. Dragging within a page, into a page (files), or out of a page share the same event model with slightly different requirements.

What does HTML markup add?

The draggable attribute marks custom sources. Images, links, and text selections are already draggable by default. A page script listens for drag events and updates the DOM on drop.

When should you use it?

Kanban cards, reordering lists, moving items between panels, or accepting file drops onto an HTML zone. For touch-first mobile apps, plan for limited native support or a dedicated library.

Drop zones need preventDefault on dragover.

Without it, the browser shows a “fly back” and never fires drop.

Text selection changes.

When an element is draggable="true", normal click-drag selects less easily—users may need Alt (or the keyboard) to select text inside it.

Data store timing.

Write with setData only in dragstart. Read with getData only in drop.

HTML Syntax for Drag and Drop

Mark a source, then wire events in a script:

draggable.html
<element draggable=" true">
HTML
<p id="p1" draggable="true">This element is draggable.</p>
<p id="target">Drop Zone</p>

<script>
  var p1 = document.getElementById("p1");
  var target = document.getElementById("target");

  p1.addEventListener("dragstart", function (ev) {
    ev.dataTransfer.setData("text/plain", ev.target.innerText);
  });

  target.addEventListener("dragover", function (ev) {
    ev.preventDefault(); // required to allow drop
  });

  target.addEventListener("drop", function (ev) {
    ev.preventDefault();
    ev.target.append(ev.dataTransfer.getData("text/plain"));
  });
</script>

Core HTML Concepts

Building blocks of drag-and-drop on a webpage:

ConceptRole
draggable="true"Makes a custom HTML element a drag source
Default draggablesImages, links, and selections (browser-defined data)
DataTransferDrag data store: text items (and files) with MIME types
setData / getDataWrite in dragstart; read in drop
Drop targetElement that calls preventDefault on dragover
DragEventEvent object passed to drag handlers (dataTransfer)

Drag events on HTML elements

EventFires when…On
dragstartDragging beginsDragged item
dragItem is moving (repeatedly)Dragged item
dragenterPointer enters an elementPotential target
dragleavePointer leaves an elementPotential target
dragoverItem over an element (repeatedly)Potential target
dropUser releases on a valid targetDrop target
dragendDrag operation endsDragged item

dragstart / drag / dragend do not fire when dragging a file into the browser from the OS. Target events do not fire when dragging out of the page.

How to Add Drag and Drop to an HTML Page

Follow this order for a working in-page drag.

  1. Add HTML for the source (draggable="true") and the drop zone.
  2. On dragstart, call dataTransfer.setData("text/plain", …) (and optional other types).
  3. On the drop zone, dragover → preventDefault() so drop is allowed.
  4. On drop, preventDefault(), getData, and update the HTML.
  5. On dragend, reset source styles (opacity, classes).
  6. Optional: highlight the zone on dragenter / dragover, clear on dragleave / drop.
GoalHTML / script pattern
Make a div draggable<div draggable="true">…</div>
Pass texte.dataTransfer.setData("text/plain", text)
Allow dropdragover → e.preventDefault()
Read on drope.dataTransfer.getData("text/plain")
Disable link drag<a draggable="false" href="…">

Live Preview

Drag the blue card onto the dashed zone. Uses the same HTML attributes and events as a real page.

demo

Source has draggable="true". Zone calls preventDefault on dragover.

Drag onto the zone
Drag me
Drop here
Waiting for drop…

HTML Examples

Six patterns: enable dragging, set data, drop zones, dragend cleanup, hover highlight, and a full demo.

Example 1 — Make an HTML Element Draggable

HTML
<div draggable="true">
  Drag me!
</div>
Try It Yourself

How It Works

  1. draggable="true" enables the browser drag gesture on this element.
  2. Default look — a ghost follows the pointer even before you add handlers.
  3. No drop yet — without a drop zone that calls preventDefault, release does nothing useful.
  4. Images/links already drag; set draggable="false" to turn that off.

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

  1. setData stores a string under a MIME type in the drag data store.
  2. Only in dragstart can you modify the store for custom elements.
  3. getData in drop reads the same type.
  4. You can set multiple types (text/plain, text/html, text/uri-list).

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

  1. dragover + preventDefault turns the element into a drop target.
  2. drop fires when the user releases over that target.
  3. preventDefault on drop avoids the browser’s default (e.g. navigating on link drops).
  4. Update HTML with the transferred string (or move a node by id).

Example 4 — dragstart and dragend

JS
el.addEventListener("dragstart", function (e) {
  e.dataTransfer.setData("text/plain", "dragging"); // helps Firefox
  el.classList.add("dragging");
});
el.addEventListener("dragend", function () {
  el.classList.remove("dragging");
});
Try It Yourself

How It Works

  1. dragstart — set data and optional “dragging” styles on the source.
  2. dragend — always runs when the gesture ends (success or cancel).
  3. Reset UI in dragend so opacity/classes do not stick.
  4. Firefox often needs at least one setData call for a smooth drag.

Example 5 — Highlight the Drop Zone

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

  1. Visual feedback tells users the zone will accept the item.
  2. dragleave clears the highlight when the pointer exits.
  3. Also clear styles on drop so the zone does not stay “over”.
  4. dragenter can start the highlight if you prefer enter/leave pairing.

Example 6 — Complete HTML Demo

Source, data, drop zone, and highlight together:

HTML
<div id="draggable" draggable="true">Drag me!</div>
<div id="dropzone">Drop here</div>
<script>
  var draggable = document.getElementById("draggable");
  var dropzone = document.getElementById("dropzone");

  draggable.addEventListener("dragstart", function (e) {
    e.dataTransfer.setData("text/plain", "Dragged Data");
  });

  dropzone.addEventListener("dragover", function (e) {
    e.preventDefault();
    dropzone.classList.add("over");
  });

  dropzone.addEventListener("dragleave", function () {
    dropzone.classList.remove("over");
  });

  dropzone.addEventListener("drop", function (e) {
    e.preventDefault();
    dropzone.classList.remove("over");
    dropzone.textContent = "Dropped data: " + e.dataTransfer.getData("text/plain");
  });
</script>
Try It Yourself

How It Works

  1. HTML defines source and target elements.
  2. dragstart puts a string into dataTransfer.
  3. dragover allows the drop and shows feedback.
  4. drop reads the string and updates the zone’s text.

Edge Cases & Pitfalls

Most “drop does nothing” bugs are missing preventDefault or wrong event timing for dataTransfer.

dragover

Forgot preventDefault on dragover

The drop never fires. Always cancel default on the intended HTML drop zone.

data

Reading data outside drop

The store is unreadable on most events. Set in dragstart; get in drop.

touch

Mobile / touch

Native HTML5 DnD is desktop-oriented. Plan pointer/touch alternatives for phones.

files

OS file drops

No dragstart on the page. Handle drop and read dataTransfer.files.

select

Text selection inside draggables

Dragging may capture the gesture. Document Alt/keyboard selection for content editors.

a11y

Keyboard accessibility

Mouse DnD alone is not enough. Offer buttons or keyboard moves for the same HTML actions.

Browser Support

The HTML Drag and Drop API (draggable, DragEvent, DataTransfer) is supported in all modern desktop browsers.

✓ Desktop · Widely available

Drag and Drop in HTML

draggable elements and drop targets work across current desktop browsers; touch support varies.

Desktop HTML DnD API
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
HTML Drag and Drop Wide

Bottom line: Build desktop HTML DnD with confidence—always preventDefault on dragover, and plan separately for touch.

Key Takeaways

  • draggable="true" marks custom HTML drag sources.
  • preventDefault on dragover is required for drops.
  • setData in dragstart; getData in drop.
  • dragend resets source styles whether the drop succeeded or not.

One line: Mark HTML with draggable → set data on dragstart → allow drop with dragover → update the page on drop.

Frequently Asked Questions

It is a browser API that lets users pick up HTML elements (or default-draggable items like images and links), drag them, and drop them onto a target. You mark sources with draggable="true", listen for drag events, and handle drop on the target.
Add the HTML attribute draggable="true". Images and links are draggable by default; other elements need this attribute. Set draggable="false" to disable dragging on images or links.
Call event.preventDefault() in the dragover handler on the drop zone. Without that, the browser refuses the drop. Also set data in dragstart with setData if you need to read it in drop.
event.dataTransfer holds the drag data store. In dragstart, use setData(type, string). In drop, use getData(type). Types are usually text/plain or text/html. You can only write during dragstart and read during drop.
dragover fires repeatedly while the item is over a potential target—use it to preventDefault and show hover styles. drop fires once on release over a valid target—read data and update the HTML.
Desktop browsers support it well. Touch support is limited. For mobile-first UIs, consider pointer events or libraries that add touch-friendly drag behavior.

Practice in the editor

Open the full drag-and-drop HTML demo and experiment with the zone.

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.

8 people found this page helpful