Skip to Content
Course content

97: The Drag and Drop API

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.