HTML Drag & Drop API
The Drag and Drop API allows users to drag elements (like images, files, or text) and drop them into another area on the page. It makes web apps more interactive.
Key Features:
- Works with HTML attributes
draggable="true"
. - Lets users drag elements and drop them on a page
- Used in file uploaders, task boards (Trello), and image galleries.
How Drag & Drop Works
- Add
draggable="true"
to make an element draggable. - Use events on draggable items:
dragstart
,drag
,dragend
. - Use events on drop targets:
dragenter
,dragover
,dragleave
,drop
. - Call
event.preventDefault()
indragover
to allow dropping. - Use
event.dataTransfer
to pass information (e.g., the dragged element’s ID or file data).
Core Concepts & Events
draggable
attribute: enables dragging on an element.
<div draggable="true">Drag me</div>
- Key events
dragstart
: set data withdataTransfer.setData(...)
dragover
: callpreventDefault()
to enable dropdrop
: read data withdataTransfer.getData(...)
dataTransfer
objectsetData(type, data)
/getData(type)
files
(when dragging files from desktop)- Optional:
setDragImage(img, x, y)
to customize the drag ghost image
Example 1: Basic Drag a Box into a Drop Zone
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Drag Box</title>
<style>
#box {
width: 80px; height: 80px;
background: tomato;
margin: 20px; cursor: grab;
}
#dropZone {
width: 200px; height: 150px;
border: 2px dashed #666;
text-align: center; padding-top: 50px;
}
</style>
</head>
<body>
<h3>Drag the box into the drop zone</h3>
<div id="box" draggable="true"></div>
<div id="dropZone">Drop here</div>
<script>
const box = document.getElementById("box");
const dropZone = document.getElementById("dropZone");
box.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text/plain", "box");
});
dropZone.addEventListener("dragover", (e) => e.preventDefault());
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
dropZone.textContent = "Box Dropped!";
});
</script>
</body>
</html>
Output :
Example 2: Drag-and-Drop File Upload (Preview Images)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File Upload</title>
<style>
#fileZone {
width: 300px; height: 150px;
border: 2px dashed #666;
text-align: center; padding-top: 50px;
margin-bottom: 10px;
}
#preview img { max-width: 100px; margin: 5px; }
</style>
</head>
<body>
<h3>Drag and drop images</h3>
<div id="fileZone">Drop images here</div>
<div id="preview"></div>
<script>
const fileZone = document.getElementById("fileZone");
const preview = document.getElementById("preview");
fileZone.addEventListener("dragover", (e) => e.preventDefault());
fileZone.addEventListener("drop", (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
for (let file of files) {
if (file.type.startsWith("image/")) {
const reader = new FileReader();
reader.onload = (event) => {
const img = document.createElement("img");
img.src = event.target.result;
preview.appendChild(img);
};
reader.readAsDataURL(file);
}
}
});
</script>
</body>
</html>
Output :
Example 3: Custom Drag Image (Ghost)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Drag Image</title>
<style>
#dragMe {
width: 100px; height: 100px;
background: teal; color: white;
text-align: center; line-height: 100px;
cursor: grab;
}
#dropTarget {
margin-top: 20px;
width: 250px; height: 120px;
border: 2px dashed gray;
text-align: center; padding-top: 40px;
}
</style>
</head>
<body>
<h3>Custom ghost image while dragging</h3>
<div id="dragMe" draggable="true">Drag me</div>
<div id="dropTarget">Drop here</div>
<script>
const dragMe = document.getElementById("dragMe");
const dropTarget = document.getElementById("dropTarget");
dragMe.addEventListener("dragstart", (e) => {
const ghost = document.createElement("div");
ghost.style.width = "80px";
ghost.style.height = "80px";
ghost.style.background = "orange";
ghost.style.borderRadius = "50%";
document.body.appendChild(ghost);
e.dataTransfer.setDragImage(ghost, 40, 40);
setTimeout(() => ghost.remove(), 0);
});
dropTarget.addEventListener("dragover", (e) => e.preventDefault());
dropTarget.addEventListener("drop", (e) => {
e.preventDefault();
dropTarget.textContent = "Dropped with custom ghost!";
});
</script>
</body>
</html>
Output :
Example 4: Sortable List (Reorder Items)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sortable List</title>
<style>
ul { list-style: none; padding: 0; }
li {
padding: 10px; margin: 5px;
background: lightblue;
cursor: move;
}
</style>
</head>
<body>
<h3>Reorder the items by dragging</h3>
<ul id="sortable">
<li draggable="true">Item 1</li>
<li draggable="true">Item 2</li>
<li draggable="true">Item 3</li>
<li draggable="true">Item 4</li>
</ul>
<script>
const sortable = document.getElementById("sortable");
let draggedItem = null;
sortable.addEventListener("dragstart", (e) => {
draggedItem = e.target;
});
sortable.addEventListener("dragover", (e) => {
e.preventDefault();
const target = e.target;
if (target.tagName === "LI" && target !== draggedItem) {
const rect = target.getBoundingClientRect();
const next = (e.clientY - rect.top) / (rect.height) > 0.5;
sortable.insertBefore(draggedItem, next ? target.nextSibling : target);
}
});
</script>
</body>
</html>
Output :
Browser Support & Limitations
- Works in all major desktop browsers.
- Mobile support is inconsistent for native DnD. Prefer Pointer Events or custom touch logic for a great mobile UX.
- File drops (from OS to browser) rely on
dataTransfer.files
.