JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
97: The Drag and Drop API
I've always found the Drag and Drop API a bit quirky. Unlike most modern JS libraries that make this feel seamless, the native browser API feels like it was designed by a committee that couldn't agree on how things should work. Let's dive in by trying to build a simple Kanban-style move: shifting a task from a "To Do" list to a "Done" list.
Wait, why won't it move?
I'll start with some basic HTML. I've got two divs acting as columns and a couple of task elements. If I try to click and drag one of these tasks right now, nothing happens. The browser just treats it like a piece of text it's trying to highlight.
<div id="todo" class="column">
<div id="task-1" class="item">Fix CSS bug</div>
</div>
<div id="done" class="column">
<div id="task-2" class="item">Write docs</div>
</div>
Turns out, elements aren't draggable by default. I need to explicitly tell the browser, "Hey, this specific thing is allowed to be picked up." I'll add the draggable="true" attribute to my items. Now, when I click and drag, I see a ghost image of the element following my cursor. Progress. But when I try to drop it into the other column? It just snaps back to where it started. Frustrating, right?
Fighting the Browser's Default Behavior
Here is the part that trips everyone up. By default, the browser actually prevents you from dropping things onto other elements. It thinks you're trying to drag a file or a link into the window, and its default response is to say "no."
To fix this, I have to intercept the dragover event on the destination column and tell the browser to stand down. I'll use preventDefault().
const columns = document.querySelectorAll('.column');
columns.forEach(column => {
column.addEventListener('dragover', (e) => {
e.preventDefault(); // This is the magic line that allows a drop
});
});
Now the browser allows the drop. But we're still just moving a ghost image. The actual DOM element is still sitting in the first column. We need a way to communicate which element is being moved from the start of the drag to the end of the drop.
Passing the ID Along
The API provides a dataTransfer object for this. Think of it like a little clipboard that travels with the cursor. When the drag starts, I'll stash the ID of the element I'm moving. When the drop happens, I'll retrieve it.
I'll add a dragstart listener to my tasks:
const tasks = document.querySelectorAll('.item');
tasks.forEach(task => {
task.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', e.target.id);
});
});
I used 'text/plain' as the format. You could use custom formats, but for a simple ID, plain text is the standard. Now the "clipboard" knows that "task-1" is currently in flight.
Making it Stick
Finally, I need to handle the drop event. This is where the actual DOM manipulation happens. I'll grab the ID from the dataTransfer object, find the element in the document, and append it to the column that received the drop.
columns.forEach(column => {
column.addEventListener('drop', (e) => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
const draggableElement = document.getElementById(id);
column.appendChild(draggableElement);
});
});
If I test this now, it works. I drag the task, the dragover prevents the browser from blocking me, the dragstart remembers which task I picked up, and the drop event physically moves the element in the DOM. It's a bit verbose, and I'll admit I hate having to call preventDefault() in two different places, but that's the native API for you.
📋 Practical Task
Build a Priority-Based Task Sorter
Create a simple interface with three columns: "Low Priority", "Medium Priority", and "High Priority". Populate each column with at least two draggable task items. Implement the Drag and Drop API so that users can move tasks between these priority levels. To make it a real challenge, add a visual cue (like a background color change) to the destination column whenever a draggable item is being held over it, and remove that cue once the item is dropped or dragged away.
There are no comments for now.