Skip to Content
Course content

192: Building a Kanban Board with Drag and Drop

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

I remember working with a junior dev a few years back who was tasked with building a Trello-style board for our internal project management tool. He spent an entire afternoon fighting with the browser, convinced there was a bug in the Chrome engine. He had the cards moving across the screen, but the second he let go over a column, the card would just snap back to its original position as if nothing had happened. He had the drop event listener perfectly written, but he missed one tiny, unintuitive detail: the dragover event. In the HTML5 Drag and Drop API, the browser's default behavior is to prevent dropping. If you don't explicitly tell the browser to stop that default behavior, your drop event will never even fire.

Building a Kanban board is the perfect way to wrap your head around this because it forces you to manage state between different containers. We aren't just moving a pixel; we're moving a piece of data from one logical state (like "To Do") to another ("Doing").

Capturing the Card's Identity

First, you have to tell the browser which elements are actually movable. You do this by adding the draggable="true" attribute to your card elements. But simply moving the element visually isn't enough; the receiving column needs to know which card is being dropped.

This is where the dataTransfer object comes in. Think of it as a temporary clipboard that follows the mouse cursor during the drag operation. In the dragstart event, we use setData() to store the ID of the card. I usually prefer using a unique id attribute on the card so that the drop target can easily find the element in the DOM.

const card = document.querySelector('.kanban-card');

card.addEventListener('dragstart', (e) => {
  // Store the ID of the element being dragged
  e.dataTransfer.setData('text/plain', e.target.id);
  e.target.classList.add('dragging');
});

card.addEventListener('dragend', (e) => {
  e.target.classList.remove('dragging');
});

Making the Columns Receptive

Now we get to the part that tripped up my colleague. To make a column a valid drop zone, you must listen for the dragover event and call event.preventDefault(). If you skip this, the browser will show a "no entry" sign on the cursor, and your drop event will be ignored.

Once the drop event fires, you retrieve the ID we stored earlier using getData(). From there, it's a simple matter of using appendChild(). Since the card already exists in the DOM, appendChild() doesn't create a duplicate; it physically moves the element from the old column to the new one.

const columns = document.querySelectorAll('.kanban-column');

columns.forEach(column => {
  column.addEventListener('dragover', (e) => {
    // This is the "magic" line that allows the drop to happen
    e.preventDefault(); 
  });

  column.addEventListener('drop', (e) => {
    e.preventDefault();
    const cardId = e.dataTransfer.getData('text/plain');
    const draggableElement = document.getElementById(cardId);
    
    // Move the card into the current column
    column.appendChild(draggableElement);
  });
});

One tip from my own experience: don't rely solely on the DOM for your state. While appendChild() handles the visual move, in a real production app, you'd want to trigger an API call inside that drop handler to update the card's status in your database. Otherwise, the second the user refreshes the page, all those carefully organized tasks will jump right back to the "To Do" column.




📋 Practical Task

Build a Sprint Backlog Task Mover

Create a functional Kanban prototype with three columns: "Backlog", "In Progress", and "Done". Each column should start with at least two task cards inside it.

Your implementation must satisfy the following requirements:

  • Cards must be draggable using the HTML5 Drag and Drop API.
  • The columns must be configured to allow cards to be dropped into them.
  • When a card is dropped into a new column, it must physically move to that container.
  • Bonus: Add a CSS class called .dragging that reduces the opacity of the card while it is being moved to give the user visual feedback.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.