Build A Drag & Drop Kanban Board using HTML CSS & JavaScript
Last Updated :
13 Mar, 2024
A Kanban board is a project management tool designed to visualize work, limit work in progress, and maximize efficiency. With drag & drop functionality, users can easily move tasks between different stages of completion, providing a visual representation of the workflow.
Final Output
Approach:
The below defined steps can be utilised to build and design a Kanban board layout:
- In the first step, we will create a folder with the project name and create the HTML, CSS, JavaScript files.
- Now, use the different HTML tags like header, meta, title, head, div, input, img logo etc to structure the web page.
- Style the different components and the elements of HTML responsively to make the page attractive for every device using CSS.
- To define the responsive styles, Adjust the width of the containers in %.
- Use JavaScript to add interactivity, including drag and drop functionality.
- Add functionality to allow users to add, edit, and delete tasks.
Example: This example describes the basic implementation of the Drag & Drop Kanban Board using HTML, CSS, and JavaScript.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content=
"width=device-width, initial-scale=1.0">
<title>Kanban Board</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href=
"https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap">
</head>
<body>
<div class="head">
<img src=
"https://media.geeksforgeeks.org/gfg-gg-logo.svg">
<h1>Kanban Board</h1>
</div>
<div class="board">
<div class="column" id="todo" ondrop="drop(event, 'todo')"
ondragover="allowDrop(event)">
<h2>Todo</h2>
<hr>
<div class="tasks-in-btn">
<input type="text" id="taskInput" class="task-input"
placeholder="Enter a task..."
oninput="capitalizeInput(this)">
<button class="add-task-btn" onclick="addTask('todo')">
Add Task
</button>
</div>
<div class="task-container"></div>
</div>
<div class="column" id="in-progress"
ondrop="drop(event, 'in-progress')"
ondragover="allowDrop(event)">
<h2>In Progress</h2>
<hr>
<div class="task-container"></div>
</div>
<div class="column" id="done"
ondrop="drop(event, 'done')"
ondragover="allowDrop(event)">
<h2>Done</h2>
<hr>
<div class="task-container"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
CSS
/* style.css */
body {
font-family: 'Roboto', sans-serif;
background-color: #f5f5f5;
margin: 0;
padding: 0;
}
.head {
background-color: #1976d2;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
box-shadow:
0px 2px 5px rgba(0, 0, 0, 0.1);
}
.head img {
width: 50px;
margin-right: 10px;
}
.head h1 {
font-size: 24px;
margin: 0;
}
.board {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
padding: 20px;
}
.column {
/* width: 30%; */
width: calc(100% - 30px);
max-width: 400px;
margin: 10px;
background-color: #fff;
border-radius: 10px;
padding: 20px;
box-shadow:
0px 2px 5px rgba(0, 0, 0, 0.1);
}
.column h2 {
font-size: 18px;
margin-bottom: 10px;
color: #333;
}
.task-input {
width: 60%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.task {
background-color: #e0e0e0;
padding: 10px;
border-radius: 5px;
margin-bottom: 10px;
cursor: move;
}
.delete-btn {
float: right;
cursor: pointer;
}
.add-task-btn {
background-color: #4caf50;
border: none;
color: white;
/* Adjust padding to make
the button smaller */
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
/* Decrease font size */
border-radius: 5px;
/* Adjust border radius
for a softer look */
cursor: pointer;
transition: background-color 0.3s;
}
.add-task-btn:hover {
background-color: #45a049;
}
#todo .task {
background-color: #b3e5fc;
/* Light blue */
}
#in-progress .task {
background-color: #ffcdd2;
/* Light pink */
}
#done .task {
background-color: #c8e6c9;
/* Light green */
}
.task {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #ccc;
border-radius: 5px;
padding: 5px;
margin-bottom: 5px;
cursor: move;
}
.delete-btn {
float: right;
cursor: pointer;
}
.tasks-in-btn {
display: flex;
justify-content: space-around;
margin-bottom: 3px;
}
Javascript
//script.js
let tasks = JSON.parse
(localStorage.getItem('tasks')) || [];
document.addEventListener
("DOMContentLoaded", function () {
renderTasks();
});
// Function to render tasks on the board
function renderTasks() {
const columns =
['todo', 'in-progress', 'done'];
columns.forEach(columnId => {
const column =
document.getElementById(columnId);
column.querySelector('.task-container').
innerHTML = '';
tasks.forEach(task => {
if (task.status === columnId) {
const taskElement =
createTaskElement(task.content, task.id);
column.querySelector('.task-container').
appendChild(taskElement);
}
});
});
}
function createTaskElement(content, id) {
const taskId = id
const task = document.createElement("div");
task.id = taskId;
task.className = "task";
task.draggable = true;
task.innerHTML =
`${content}
<span class="delete-btn"
onclick="deleteTask('${taskId}')">
❌
</span>`;
task.addEventListener("dragstart", drag);
return task;
}
// Function to delete a task
function deleteTask(taskId) {
tasks = tasks.
filter(task => task.id !== taskId);
updateLocalStorage();
renderTasks();
}
function allowDrop(event) {
event.preventDefault();
}
function drag(event) {
event.dataTransfer.
setData("text/plain", event.target.id);
}
function drop(event, columnId) {
event.preventDefault();
console.log(columnId)
const data = event.
dataTransfer.getData("text/plain");
const draggedElement =
document.getElementById(data);
console.log(draggedElement)
if (draggedElement) {
const taskStatus = columnId;
updateTaskStatus(data, taskStatus);
event.target.querySelector('.task-container').
appendChild(draggedElement);
}
}
function capitalizeInput(input) {
input.value = input.value.toUpperCase();
}
function addTask(columnId) {
const taskInput =
document.getElementById('taskInput');
const taskContent = taskInput.value.trim();
if (taskContent !== "") {
const newTask = {
id: "task-" + Date.now(),
content: taskContent,
status: columnId
};
tasks.push(newTask);
updateLocalStorage();
renderTasks();
taskInput.value = "";
}
}
// Function to update task status
// when moved to another column
function updateTaskStatus(taskId, newStatus) {
console.log(newStatus)
tasks = tasks.map(task => {
console.log(task)
console.log(taskId)
if (task.id === taskId) {
console.log("inside if")
return { ...task, status: newStatus };
}
return task;
});
updateLocalStorage();
}
// Function to update local
// storage with current tasks
function updateLocalStorage() {
console.log("task update")
localStorage.setItem
('tasks', JSON.stringify(tasks));
}
Output:
final output gif
Share your thoughts in the comments
Please Login to comment...