The HTML5 Drag and Drop API allows users to drag elements around the page and drop them into designated areas. This creates intuitive, interactive interfaces for tasks like file uploads, list reordering, and visual organization.
Making Elements Draggable
To make an element draggable, simply add the draggable="true" attribute:
<div draggable="true">
Drag me!
</div>
<img src="photo.jpg" draggable="true" alt="Draggable image">
<p draggable="true">This paragraph can be dragged</p>By default, images and links are draggable. Other elements need the draggable attribute set explicitly.
Drag Events
The drag and drop process involves several events that fire on both the dragged element and the drop target:
Events on the Draggable Element
- dragstart: Fires when the user starts dragging
- drag: Fires continuously while dragging
- dragend: Fires when dragging stops
Events on the Drop Target
- dragenter: Fires when dragged item enters the drop zone
- dragover: Fires continuously while item is over drop zone
- dragleave: Fires when item leaves the drop zone
- drop: Fires when item is dropped
Basic Drag and Drop Example
Here's a complete example showing how to implement drag and drop:
<!DOCTYPE html>
<html>
<head>
<style>
.draggable {
width: 100px;
height: 100px;
background: #3b82f6;
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
margin: 10px;
}
.drop-zone {
width: 300px;
height: 200px;
border: 3px dashed #ccc;
display: flex;
align-items: center;
justify-content: center;
margin: 20px;
}
.drop-zone.drag-over {
background: #f0f9ff;
border-color: #3b82f6;
}
</style>
</head>
<body>
<div class="draggable" draggable="true" id="item1">
Drag me
</div>
<div class="drop-zone" id="dropZone">
Drop here
</div>
<script>
const draggable = document.getElementById('item1');
const dropZone = document.getElementById('dropZone');
// Drag start
draggable.addEventListener('dragstart', function(e) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
this.style.opacity = '0.5';
});
// Drag end
draggable.addEventListener('dragend', function(e) {
this.style.opacity = '1';
});
// Prevent default to allow drop
dropZone.addEventListener('dragover', function(e) {
e.preventDefault();
this.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', function(e) {
this.classList.remove('drag-over');
});
// Handle drop
dropZone.addEventListener('drop', function(e) {
e.preventDefault();
this.classList.remove('drag-over');
this.innerHTML = e.dataTransfer.getData('text/html');
});
</script>
</body>
</html>The DataTransfer Object
The dataTransfer object is central to drag and drop. It allows you to store and retrieve data during the operation:
// Setting data during dragstart
element.addEventListener('dragstart', function(e) {
// Store data with a type identifier
e.dataTransfer.setData('text/plain', 'Some text');
e.dataTransfer.setData('text/html', '<p>Some HTML</p>');
// Set the drag effect
e.dataTransfer.effectAllowed = 'move'; // 'copy', 'link', 'move', 'all'
});
// Getting data during drop
dropZone.addEventListener('drop', function(e) {
e.preventDefault();
// Retrieve data by type
const text = e.dataTransfer.getData('text/plain');
const html = e.dataTransfer.getData('text/html');
console.log('Dropped:', text);
});Custom Drag Image
You can customize what users see while dragging:
element.addEventListener('dragstart', function(e) {
// Create a custom drag image
const dragImage = document.createElement('div');
dragImage.textContent = 'Dragging...';
dragImage.style.position = 'absolute';
dragImage.style.top = '-1000px';
document.body.appendChild(dragImage);
// Set the custom image (element, xOffset, yOffset)
e.dataTransfer.setDragImage(dragImage, 0, 0);
// Clean up after drag
setTimeout(() => document.body.removeChild(dragImage), 0);
});Dragging Files
One of the most common uses of drag and drop is file uploads. Here's how to handle dropped files:
<!DOCTYPE html>
<html>
<head>
<style>
.file-drop-zone {
width: 400px;
height: 200px;
border: 3px dashed #ccc;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 20px;
}
.file-drop-zone.active {
border-color: #3b82f6;
background: #f0f9ff;
}
</style>
</head>
<body>
<div class="file-drop-zone" id="fileDropZone">
<p>Drag files here to upload</p>
</div>
<div id="fileList"></div>
<script>
const dropZone = document.getElementById('fileDropZone');
const fileList = document.getElementById('fileList');
// Prevent default drag behavior
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Highlight drop zone
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, () => {
dropZone.classList.add('active');
});
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, () => {
dropZone.classList.remove('active');
});
});
// Handle dropped files
dropZone.addEventListener('drop', function(e) {
const files = e.dataTransfer.files;
fileList.innerHTML = '<h3>Files dropped:</h3>';
Array.from(files).forEach(file => {
fileList.innerHTML += ``;
});
});
</script>
</body>
</html>Sortable List Example
Here's how to create a drag-and-drop sortable list:
<!DOCTYPE html>
<html>
<head>
<style>
.sortable-list {
list-style: none;
padding: 0;
width: 300px;
}
.sortable-item {
padding: 15px;
margin: 5px 0;
background: #f3f4f6;
border: 2px solid #e5e7eb;
cursor: move;
}
.sortable-item.dragging {
opacity: 0.5;
}
</style>
</head>
<body>
<ul class="sortable-list" id="sortableList">
<li class="sortable-item" draggable="true">Item 1</li>
<li class="sortable-item" draggable="true">Item 2</li>
<li class="sortable-item" draggable="true">Item 3</li>
<li class="sortable-item" draggable="true">Item 4</li>
</ul>
<script>
const list = document.getElementById('sortableList');
let draggedItem = null;
list.addEventListener('dragstart', function(e) {
draggedItem = e.target;
e.target.classList.add('dragging');
});
list.addEventListener('dragend', function(e) {
e.target.classList.remove('dragging');
});
list.addEventListener('dragover', function(e) {
e.preventDefault();
const afterElement = getDragAfterElement(list, e.clientY);
if (afterElement == null) {
list.appendChild(draggedItem);
} else {
list.insertBefore(draggedItem, afterElement);
}
});
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.sortable-item:not(.dragging)')];
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
</script>
</body>
</html>Best Practices
- Always call preventDefault(): On dragover and drop events to enable dropping
- Provide Visual Feedback: Show users where they can drop items
- Use Appropriate Cursors: cursor: move for draggable items
- Handle Touch Devices: Consider adding touch event alternatives for mobile
- Accessibility: Provide keyboard alternatives for drag and drop operations
- Clear Data After Use: Clean up dataTransfer data when drag ends
Browser Support
The Drag and Drop API is well-supported in all modern browsers. However, implementation details can vary slightly, so always test across different browsers.
Key Takeaways
- Use
draggable="true"to make elements draggable - Drag and drop involves multiple events: dragstart, drag, dragend, dragenter, dragover, dragleave, and drop
- The dataTransfer object stores data during drag operations
- Always call
preventDefault()on dragover and drop events - Provide visual feedback for better user experience
- File drops are handled through dataTransfer.files
- Consider accessibility and touch device support
What's Next?
You now understand how to implement drag and drop functionality in your web applications. In the next lesson, we'll explore Local Storage, which allows you to store data in the browser for persistent user experiences.