Drag and Drop API
The Drag and Drop API allows users to move elements by dragging and dropping them.
Drag and Drop Basics
Creating a Draggable Element
<div draggable="true" id="draggable"
ondragstart="drag(event)">
Drag me!
</div>
<div id="dropzone"
ondragover="event.preventDefault()"
ondrop="drop(event)">
Jatuhkan di sini
</div>
<script>
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
const data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
</script>
Drag and Drop Events
Event Targets
<div id="source" draggable="true">Drag me</div>
<div id="target">Drop here</div>
<script>
const source = document.getElementById('source');
const target = document.getElementById('target');
// Events on the element being dragged
source.addEventListener('dragstart', function(e) {
e.dataTransfer.setData('text/plain', 'Hello');
e.target.style.opacity = '0.4';
});
source.addEventListener('dragend', function(e) {
e.target.style.opacity = '1';
});
// Events on the drop target
target.addEventListener('dragover', function(e) {
e.preventDefault();
e.target.style.background = '#3498db';
});
target.addEventListener('dragleave', function(e) {
e.target.style.background = '';
});
target.addEventListener('drop', function(e) {
e.preventDefault();
e.target.style.background = '';
const data = e.dataTransfer.getData('text/plain');
alert('Dropped: ' + data);
});
</script>
Case Study: Campus Drag and Drop Dashboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Drag and Drop</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
background: #f0f2f5;
padding: 2rem;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
h1 {
color: #1e3a5f;
margin-bottom: 2rem;
text-align: center;
}
.board {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
.column {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
min-height: 400px;
}
.column-header {
padding: 1rem;
border-bottom: 1px solid #eee;
font-weight: bold;
color: #1e3a5f;
}
.column-header.todo {
border-bottom: 3px solid #3498db;
}
.column-header.progress {
border-bottom: 3px solid #f39c12;
}
.column-header.done {
border-bottom: 3px solid #2ecc71;
}
.column-content {
padding: 1rem;
min-height: 300px;
}
.task {
background: #f8f9fa;
padding: 1rem;
border-radius: 6px;
margin-bottom: 0.8rem;
cursor: grab;
border-left: 4px solid #3498db;
transition: all 0.2s;
}
.task:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
.task.dragging {
opacity: 0.5;
cursor: grabbing;
}
.task h3 {
font-size: 0.95rem;
color: #333;
margin-bottom: 0.5rem;
}
.task p {
font-size: 0.85rem;
color: #666;
margin-bottom: 0.5rem;
}
.task .priority {
font-size: 0.75rem;
padding: 0.2rem 0.5rem;
border-radius: 3px;
display: inline-block;
}
.priority.high {
background: #fee2e2;
color: #dc2626;
}
.priority.medium {
background: #fef3c7;
color: #d97706;
}
.priority.low {
background: #d1fae5;
color: #059669;
}
.column-content.dragover {
background: #e3f2fd;
}
.add-task {
display: block;
width: 100%;
padding: 0.8rem;
margin-top: 1rem;
background: #f8f9fa;
border: 2px dashed #ddd;
border-radius: 6px;
cursor: pointer;
color: #666;
font-size: 0.9rem;
}
.add-task:hover {
background: #e3f2fd;
border-color: #3498db;
}
</style>
</head>
<body>
<div class="container">
<h1>Campus Task Management</h1>
<div class="board">
<!-- Todo Column -->
<div class="column">
<div class="column-header todo">To Do</div>
<div class="column-content" id="todo"
ondrop="drop(event)"
ondragover="event.preventDefault()"
ondragenter="dragEnter(event)"
ondragleave="dragLeave(event)">
<div class="task" draggable="true"
ondragstart="drag(event)"
id="task1">
<h3>Create HTML Assignment</h3>
<p>Deadline: January 15, 2024</p>
<span class="priority high">High/span>
</div>
<div class="task" draggable="true"
ondragstart="drag(event)"
id="task2">
<h3>Group Presentation</h3>
<p>Deadline: January 20, 2024</p>
<span class="priority medium">Sedang</span>
</div>
<button class="add-task">+ Add Task</button>
</div>
</div>
<!-- In Progress Column -->
<div class="column">
<div class="column-header progress">In Progress</div>
<div class="column-content" id="progress"
ondrop="drop(event)"
ondragover="event.preventDefault()"
ondragenter="dragEnter(event)"
ondragleave="dragLeave(event)">
<div class="task" draggable="true"
ondragstart="drag(event)"
id="task3">
<h3>Write Thesis Chapter 3</h3>
<p>Deadline: February 10, 2024</p>
<span class="priority high">High/span>
</div>
<button class="add-task">+ Add Task</button>
</div>
</div>
<!-- Done Column -->
<div class="column">
<div class="column-header done">Done</div>
<div class="column-content" id="done"
ondrop="drop(event)"
ondragover="event.preventDefault()"
ondragenter="dragEnter(event)"
ondragleave="dragLeave(event)">
<div class="task" draggable="true"
ondragstart="drag(event)"
id="task4">
<h3>Database Midterm</h3>
<p>Done: January 5, 2024</p>
<span class="priority low">Low/span>
</div>
<button class="add-task">+ Add Task</button>
</div>
</div>
</div>
</div>
<script>
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
ev.target.classList.add('dragging');
}
document.addEventListener('dragend', function(e) {
e.target.classList.remove('dragging');
});
function drop(ev) {
ev.preventDefault();
const data = ev.dataTransfer.getData("text");
const task = document.getElementById(data);
if (task) {
// Remove button from parent if exists
const addBtn = ev.target.querySelector('.add-task');
ev.target.appendChild(task);
ev.target.classList.remove('dragover');
// Re-add button at end
if (addBtn) {
ev.target.appendChild(addBtn);
}
}
}
function dragEnter(ev) {
ev.preventDefault();
ev.target.classList.add('dragover');
}
function dragLeave(ev) {
ev.target.classList.remove('dragover');
}
</script>
</body>
</html>
Everyday Case Study
Case: A developer needs to create a sortable list.
<ul id="sortable">
<li draggable="true">Item 1</li>
<li draggable="true">Item 2</li>
<li draggable="true">Item 3</li>
</ul>
<script>
let draggedItem = null;
document.querySelectorAll('#sortable li').forEach(item => {
item.addEventListener('dragstart', function(e) {
draggedItem = this;
setTimeout(() => this.style.display = 'none', 0);
});
item.addEventListener('dragend', function(e) {
setTimeout(() => this.style.display = 'block', 0);
draggedItem = null;
});
item.addEventListener('dragover', function(e) {
e.preventDefault();
const afterElement = getDragAfterElement(document.getElementById('sortable'), e.clientY);
const sortable = document.getElementById('sortable');
if (afterElement == null) {
sortable.appendChild(draggedItem);
} else {
sortable.insertBefore(draggedItem, afterElement);
}
});
});
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('li: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>
Best Practices
- Visual feedback: Provide indicators while dragging and dropping
- DataTransfer: Use it to carry data
- Prevent default: Essential for drop targets
- Accessibility: Provide non-drag alternatives
- Mobile support: Consider touch events
Summary
The Drag and Drop API enables natural drag-and-drop interactions. It's perfect for kanban boards, sortable lists, and other interactive interfaces.