← Back to Tutorials
JavaScript

Build a Task Manager App

Difficulty: Intermediate Est. Time: ~3 hours

Introduction

Welcome to this tutorial! In this project, you'll build a fully functional Task Manager application using vanilla JavaScript, HTML, and CSS. By the end of this tutorial, you'll have created an app where you can add, edit, delete, and organize tasks with drag-and-drop functionality.

What You'll Learn
  • DOM manipulation with vanilla JavaScript
  • Local storage for data persistence
  • Drag and drop API
  • Modern CSS layouts with Flexbox and Grid

Project Overview

Our Task Manager will include these features:

  • Add new tasks with title and description
  • Mark tasks as complete
  • Edit existing tasks
  • Delete tasks
  • Reorder tasks with drag and drop
  • Filter tasks (All, Active, Completed)
  • Data persistence using Local Storage

Setting Up the Project

Create a new folder for your project and add three files:

  • index.html
  • style.css
  • app.js

Open the folder in your code editor and let's start building!

HTML Structure

Let's create the HTML structure for our task manager:

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Task Manager</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="app-container">
        <header>
            <h1>Task Manager</h1>
        </header>
        
        <form id="task-form">
            <input type="text" id="task-input" placeholder="Add a new task..." required>
            <button type="submit">Add Task</button>
        </form>

        <div class="filters">
            <button class="filter-btn active" data-filter="all">All</button>
            <button class="filter-btn" data-filter="active">Active</button>
            <button class="filter-btn" data-filter="completed">Completed</button>
        </div>

        <ul id="task-list" class="task-list"></ul>
    </div>

    <script src="app.js"></script>
</body>
</html>

Styling the App

Now let's add some modern styling to make our app look professional:

CSS
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    padding: 2rem;
}

.app-container {
    max-width: 600px;
    margin: 0 auto;
    background: white;
    border-radius: 12px;
    padding: 2rem;
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}

h1 {
    text-align: center;
    color: #333;
    margin-bottom: 1.5rem;
}

#task-form {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

#task-input {
    flex: 1;
    padding: 0.75rem;
    border: 2px solid #e0e0e0;
    border-radius: 8px;
    font-size: 1rem;
}

#task-form button {
    padding: 0.75rem 1.5rem;
    background: #667eea;
    color: white;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-weight: 600;
}

JavaScript Logic

Now let's add the JavaScript to make our app functional. We'll implement task management with local storage persistence:

JavaScript
// Get elements from DOM
const taskForm = document.getElementById('task-form');
const taskInput = document.getElementById('task-input');
const taskList = document.getElementById('task-list');
const filterBtns = document.querySelectorAll('.filter-btn');

// State
let tasks = [];
let currentFilter = 'all';

// Load tasks from local storage
function loadTasks() {
    const stored = localStorage.getItem('tasks');
    tasks = stored ? JSON.parse(stored) : [];
    renderTasks();
}

// Save tasks to local storage
function saveTasks() {
    localStorage.setItem('tasks', JSON.stringify(tasks));
}

// Render tasks based on filter
function renderTasks() {
    taskList.innerHTML = '';
    
    const filtered = tasks.filter(task => {
        if (currentFilter === 'active') return !task.completed;
        if (currentFilter === 'completed') return task.completed;
        return true;
    });
    
    filtered.forEach((task, index) => {
        const li = document.createElement('li');
        li.className = `task-item ${task.completed ? 'completed' : ''}`;
        li.draggable = true;
        li.dataset.index = index;
        
        li.innerHTML = <?>`
            <span class="task-text">${task.text}</span>
            <div class="task-actions">
                <button onclick="toggleTask(${task.id})">
                    ${task.completed ? '↩' : '✓'}</button>
                <button onclick="deleteTask(${task.id})">✕</button>
            </div>
        `;
        
        taskList.appendChild(li);
    });
}

// Add new task
taskForm.addEventListener('submit', (e) => {
    e.preventDefault();
    const text = taskInput.value.trim();
    if (!text) return;
    
    tasks.push({
        id: Date.now(),
        text: text,
        completed: false
    });
    
    taskInput.value = '';
    saveTasks();
    renderTasks();
});

Adding Drag and Drop

Let's add drag and drop functionality to reorder tasks:

JavaScript
// Drag and drop functionality
taskList.addEventListener('dragstart', (e) => {
    if (e.target.classList.contains('task-item')) {
        e.target.classList.add('dragging');
    }
});

taskList.addEventListener('dragend', (e) => {
    if (e.target.classList.contains('task-item')) {
        e.target.classList.remove('dragging');
        saveTasks();
    }
});

taskList.addEventListener('dragover', (e) => {
    e.preventDefault();
    const afterElement = getDragAfterElement(taskList, e.clientY);
    const draggable = document.querySelector('.dragging');
    if (afterElement == null) {
        taskList.appendChild(draggable);
    } else {
        taskList.insertBefore(draggable, afterElement);
    }
});

function getDragAfterElement(container, y) {
    const draggableElements = [...container.querySelectorAll('.task-item:not(.dragging)')];
    
    return draggableElements.reduce((closest, child) => {
        const box = child.getBoundingClientRect();
        const offset = y - box.top - box.height / 2;
        
        if (offset < 0 && offset > closest.offset) {
            return { offset: offset, element: child };
        } else {
            return closest;
        }
    }, { offset: Number.NEGATIVE_INFINITY }).element;
}
Great progress!

You've now built a fully functional task manager with local storage persistence and drag-and-drop reordering. The app will remember your tasks even after you refresh the page.

Conclusion

Congratulations! You've completed the Task Manager tutorial. Here's what you built:

  • A clean, modern UI with CSS styling
  • Task CRUD operations (Create, Read, Update, Delete)
  • Local storage for data persistence
  • Filter functionality (All, Active, Completed)
  • Drag and drop reordering

Next Steps

Try adding these features on your own:

  • Add due dates to tasks
  • Implement task categories/tags
  • Add task priority levels
  • Create a dark mode toggle