# How to Use the Drag and Drop API for Kanban Board Functionality: A Complete Implementation Guide

> Build a Kanban board with the HTML Drag and Drop API. Learn to implement draggable elements, handle events, and use localStorage for persistent state. Get the complete guide.

- Repository: [Jisan Mia/dom-projects](https://github.com/jisan-mia/dom-projects)
- Tags: how-to-guide
- Published: 2026-03-04

---

**Implement a fully functional Kanban board by combining `draggable="true"` attributes with the native HTML Drag and Drop API event handlers (`ondragstart`, `ondragover`, `ondrop`), then persist the board state across sessions using `localStorage`.**

The **jisan-mia/dom-projects** repository contains a production-ready Kanban demonstration that builds an interactive task board using only vanilla HTML, CSS, and JavaScript. This implementation leverages the browser's native Drag and Drop API to enable seamless card movement between columns while maintaining state persistence without external frameworks or libraries.

## Setting Up Draggable Cards and Drop Zones

To initiate drag operations, each task card must be explicitly marked as draggable and wired to event handlers that record the element being moved.

### Making Task Cards Draggable

In [`projects/kanban/index.html`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/index.html), each task is rendered as a list item with the `draggable` attribute and an `ondragstart` handler:

```html
<li class="drag-item"
    draggable="true"
    ondragstart="drag(event)"
    contenteditable="true"
    onfocusout="updateItem(/*index*/, /*column*/)">
  Sample task
</li>

```

The `drag()` function in [`projects/kanban/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/script.js) captures the dragged element:

```javascript
function drag(e) {
  draggedItem = e.target;      // <li class="drag-item">
  isDragging = true;
}

```

### Configuring Column Drop Zones

Each Kanban column uses an unordered list (`<ul>`) as the drop container. The markup attaches three critical event handlers to manage the drop lifecycle:

```html
<ul class="dragndrop-item-list"
    id="backlog-list"
    ondrop="dropItem(event)"
    ondragover="allowDrop(event)"
    ondragenter="dragEnter(0)">
</ul>

```

- **`ondragover`**: Fires continuously while dragging over the element
- **`ondragenter`**: Fires once when entering the drop zone
- **`ondrop`**: Fires when the user releases the draggable element

## Handling Core Drag and Drop Events

The JavaScript implementation in [`projects/kanban/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/script.js) coordinates the movement of cards between the four columns: Backlog, Progress, Complete, and On-Hold.

### Allowing Drops and Providing Visual Feedback

By default, browsers prevent dropping onto elements. The `allowDrop()` function overrides this behavior:

```javascript
function allowDrop(e) {
  e.preventDefault();         // Needed otherwise drop is blocked
}

```

Visual feedback is triggered via the `dragEnter()` function, which highlights the target column by adding an `over` class:

```javascript
function dragEnter(columnIndex) {
  listColumns[columnIndex].classList.add("over");
  currentColumn = columnIndex;
}

```

The corresponding styles in [`projects/kanban/style.css`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/style.css) define the highlight appearance:

```css
.dragndrop-item-list.over {
  background-color: #f0f8ff;   /* light highlight when a column accepts a drop */
}

```

### Executing the Drop Operation

When a user releases a card, the `dropItem()` function moves the DOM element and synchronizes the underlying data:

```javascript
function dropItem(e) {
  e.preventDefault();
  listColumns.forEach(col => col.classList.remove("over")); // clear highlights

  const parent = listColumns[currentColumn];
  parent.appendChild(draggedItem);   // move the <li> in the DOM
  isDragging = false;
  rebuildArrays();                  // sync arrays <→ DOM <→ localStorage
}

```

## Persisting Board State with localStorage

The Kanban board maintains four parallel arrays (`backlogListArray`, `progressListArray`, `completeListArray`, `onHoldListArray`) that mirror the DOM state. This architecture enables reliable data persistence across browser sessions.

### Rebuilding Arrays After Movement

After each drop operation, `rebuildArrays()` reconstructs the column arrays based on the current DOM order. This ensures the JavaScript data structures remain synchronized with the visual layout before saving.

### Saving to localStorage

The `updateSavedColumns()` function persists the arrays to `localStorage` using JSON serialization:

```javascript
function updateSavedColumns() {
  listArray = [backlogListArray, progressListArray, completeListArray, onHoldListArray];
  const listNames = ["backlog", "progress", "complete", "onHold"];
  listNames.forEach((name, i) => {
    localStorage.setItem(`${name}Items`, JSON.stringify(listArray[i]));
  });
}

```

On page initialization, `updateDOM()` retrieves these values or seeds default arrays, ensuring the board state survives page refreshes.

## Adding Interactive Features

Beyond basic drag and drop functionality, the implementation supports inline editing and dynamic card creation.

### Inline Card Editing

Each `<li>` element utilizes the `contenteditable` attribute, allowing users to modify task text directly. The `onfocusout` handler triggers `updateItem()` to propagate changes back to the corresponding array index and immediately persist the update via `updateSavedColumns()`.

### Creating New Cards

The board includes a hidden input area toggled by an "Add item" button. When users save new content, `addItemToColumn()` appends the text to the appropriate column array and refreshes the interface:

```javascript
function addItemToColumn(columnIndex) {
  const itemText = addItems[columnIndex].textContent;
  const selectedArray = listArray[columnIndex];
  selectedArray.push(itemText);
  addItems[columnIndex].textContent = "";
  updateDOM();
}

```

## Styling the Drag Experience

The CSS in [`projects/kanban/style.css`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/style.css) enhances usability through cursor states and visual transitions:

```css
.drag-item {
  cursor: grab;
}
.drag-item:active {
  cursor: grabbing;
}

```

The `.over` class applied during `dragEnter` provides immediate spatial feedback, helping users identify valid drop targets before releasing the mouse button.

## Summary

- **Native API Only**: The implementation uses the standard HTML Drag and Drop API (`draggable`, `ondragstart`, `ondragover`, `ondrop`) without external dependencies.
- **Four-Column Structure**: The board manages Backlog, Progress, Complete, and On-Hold columns through parallel arrays and DOM synchronization.
- **Event Flow**: `drag()` records the source element, `allowDrop()` enables the target zone, `dragEnter()` highlights the destination, and `dropItem()` executes the move while triggering persistence.
- **State Persistence**: `localStorage` integration via `updateSavedColumns()` and `rebuildArrays()` ensures data survives browser refreshes.
- **File Locations**: Core logic resides in [`projects/kanban/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/script.js), markup in [`projects/kanban/index.html`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/index.html), and styling in [`projects/kanban/style.css`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/style.css).

## Frequently Asked Questions

### How do I prevent the browser from blocking drop operations?

You must call `event.preventDefault()` inside the `ondragover` handler. According to the HTML Drag and Drop API specification, dropping is disabled by default on most elements unless this method explicitly allows the operation, as implemented in the `allowDrop()` function within [`projects/kanban/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/script.js).

### What is the purpose of the `rebuildArrays()` function in the Kanban implementation?

The `rebuildArrays()` function iterates through the DOM elements in each column after a drop event and reconstructs the four JavaScript arrays (`backlogListArray`, `progressListArray`, etc.) to match the current visual order. This synchronization step is essential before calling `updateSavedColumns()` to persist the correct state to `localStorage`.

### Can I implement this Kanban board without using localStorage?

Yes. While the `jisan-mia/dom-projects` implementation uses `localStorage` for persistence, you can remove the `updateSavedColumns()` calls and maintain state purely in memory using the four column arrays. Alternatively, you could replace the localStorage logic with API calls to a backend server by modifying the persistence layer in [`projects/kanban/script.js`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/script.js) while retaining the same drag-and-drop event handlers.

### How does the visual feedback work when dragging cards between columns?

The `dragEnter()` function applies an `over` CSS class to the target column's `<ul>` element when a draggable card enters its boundaries. The stylesheet in [`projects/kanban/style.css`](https://github.com/jisan-mia/dom-projects/blob/main/projects/kanban/style.css) defines `.dragndrop-item-list.over` with a distinct background color (`#f0f8ff`), creating immediate visual confirmation of valid drop zones. The `dropItem()` function removes this class from all columns after the drop completes.