DEV Community

Darshil Mahraur
Darshil Mahraur

Posted on

Building a Smart Task Manager with JavaScript: From Concept to Code

Building a Smart Task Manager with JavaScript: From Concept to Code

In this tutorial, we'll build a feature-rich task manager application using vanilla JavaScript. This project will demonstrate key JavaScript concepts including DOM manipulation, local storage, event handling, and modern ES6+ features.

🎯 What We're Building

Our task manager will include:

  • Add, edit, and delete tasks
  • Task categories and priorities
  • Due date tracking
  • Local storage persistence
  • Search and filter functionality
  • Responsive design

🏗️ Project Structure

task-manager/ ├── index.html ├── styles.css ├── script.js └── README.md 
Enter fullscreen mode Exit fullscreen mode

💻 The JavaScript Implementation

Let's dive into the core JavaScript functionality:

1. Task Class and Data Structure

class Task { constructor(id, title, description, category, priority, dueDate, completed = false) { this.id = id; this.title = title; this.description = description; this.category = category; this.priority = priority; this.dueDate = dueDate; this.completed = completed; this.createdAt = new Date().toISOString(); } // Method to check if task is overdue isOverdue() { if (!this.dueDate || this.completed) return false; return new Date(this.dueDate) < new Date(); } // Method to get days until due getDaysUntilDue() { if (!this.dueDate) return null; const diffTime = new Date(this.dueDate) - new Date(); return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); } } 
Enter fullscreen mode Exit fullscreen mode

2. TaskManager Class - The Heart of Our Application

class TaskManager { constructor() { this.tasks = this.loadTasks(); this.currentFilter = 'all'; this.currentSort = 'dueDate'; this.initializeEventListeners(); this.renderTasks(); } // Load tasks from localStorage loadTasks() { const savedTasks = localStorage.getItem('tasks'); if (savedTasks) { return JSON.parse(savedTasks).map(taskData => new Task( taskData.id, taskData.title, taskData.description, taskData.category, taskData.priority, taskData.dueDate, taskData.completed ) ); } return []; } // Save tasks to localStorage saveTasks() { localStorage.setItem('tasks', JSON.stringify(this.tasks)); } // Add a new task addTask(taskData) { const task = new Task( Date.now().toString(), taskData.title, taskData.description, taskData.category, taskData.priority, taskData.dueDate ); this.tasks.push(task); this.saveTasks(); this.renderTasks(); // Show success notification this.showNotification('Task added successfully!', 'success'); } // Update an existing task updateTask(id, updates) { const taskIndex = this.tasks.findIndex(task => task.id === id); if (taskIndex !== -1) { Object.assign(this.tasks[taskIndex], updates); this.saveTasks(); this.renderTasks(); this.showNotification('Task updated successfully!', 'success'); } } // Delete a task deleteTask(id) { this.tasks = this.tasks.filter(task => task.id !== id); this.saveTasks(); this.renderTasks(); this.showNotification('Task deleted successfully!', 'info'); } // Toggle task completion toggleTask(id) { const task = this.tasks.find(task => task.id === id); if (task) { task.completed = !task.completed; this.saveTasks(); this.renderTasks(); } } // Filter tasks based on status filterTasks(filter) { this.currentFilter = filter; this.renderTasks(); } // Get filtered tasks getFilteredTasks() { let filteredTasks = [...this.tasks]; // Apply status filter switch (this.currentFilter) { case 'active': filteredTasks = filteredTasks.filter(task => !task.completed); break; case 'completed': filteredTasks = filteredTasks.filter(task => task.completed); break; case 'overdue': filteredTasks = filteredTasks.filter(task => task.isOverdue()); break; } // Sort tasks filteredTasks.sort((a, b) => { switch (this.currentSort) { case 'dueDate': if (!a.dueDate && !b.dueDate) return 0; if (!a.dueDate) return 1; if (!b.dueDate) return -1; return new Date(a.dueDate) - new Date(b.dueDate); case 'priority': const priorityOrder = { high: 3, medium: 2, low: 1 }; return priorityOrder[b.priority] - priorityOrder[a.priority]; case 'created': return new Date(b.createdAt) - new Date(a.createdAt); default: return 0; } }); return filteredTasks; } } 
Enter fullscreen mode Exit fullscreen mode

3. DOM Manipulation and Rendering

// Render tasks to the DOM renderTasks() { const taskList = document.getElementById('task-list'); const filteredTasks = this.getFilteredTasks(); taskList.innerHTML = ''; if (filteredTasks.length === 0) { taskList.innerHTML = ` <div class="empty-state"> <h3>No tasks found</h3> <p>Add a new task to get started!</p> </div> `; return; } filteredTasks.forEach(task => { const taskElement = this.createTaskElement(task); taskList.appendChild(taskElement); }); this.updateTaskStats(); } // Create individual task element createTaskElement(task) { const taskEl = document.createElement('div'); taskEl.className = `task-item ${task.completed ? 'completed' : ''} ${task.isOverdue() ? 'overdue' : ''}`; taskEl.dataset.taskId = task.id; const daysUntilDue = task.getDaysUntilDue(); const dueDateDisplay = task.dueDate ? `<span class="due-date ${task.isOverdue() ? 'overdue' : ''}"> ${task.isOverdue() ? 'Overdue' : daysUntilDue === 0 ? 'Due today' : `${daysUntilDue} days left`} </span>` : ''; taskEl.innerHTML = ` <div class="task-content"> <div class="task-header"> <input type="checkbox" class="task-checkbox" ${task.completed ? 'checked' : ''}> <h3 class="task-title">${task.title}</h3> <span class="task-priority priority-${task.priority}">${task.priority.toUpperCase()}</span> </div> <p class="task-description">${task.description}</p> <div class="task-meta"> <span class="task-category">${task.category}</span> ${dueDateDisplay} </div> </div> <div class="task-actions"> <button class="btn btn-sm btn-secondary edit-task">Edit</button> <button class="btn btn-sm btn-danger delete-task">Delete</button> </div> `; return taskEl; } 
Enter fullscreen mode Exit fullscreen mode

4. Event Handling

// Initialize all event listeners initializeEventListeners() { // Task form submission document.getElementById('task-form').addEventListener('submit', (e) => { e.preventDefault(); this.handleTaskSubmission(e.target); }); // Filter buttons document.querySelectorAll('.filter-btn').forEach(btn => { btn.addEventListener('click', (e) => { document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active')); e.target.classList.add('active'); this.filterTasks(e.target.dataset.filter); }); }); // Sort dropdown document.getElementById('sort-select').addEventListener('change', (e) => { this.currentSort = e.target.value; this.renderTasks(); }); // Search functionality document.getElementById('search-input').addEventListener('input', (e) => { this.searchTasks(e.target.value); }); // Task list event delegation document.getElementById('task-list').addEventListener('click', (e) => { const taskItem = e.target.closest('.task-item'); if (!taskItem) return; const taskId = taskItem.dataset.taskId; if (e.target.classList.contains('task-checkbox')) { this.toggleTask(taskId); } else if (e.target.classList.contains('edit-task')) { this.editTask(taskId); } else if (e.target.classList.contains('delete-task')) { this.confirmDeleteTask(taskId); } }); } 
Enter fullscreen mode Exit fullscreen mode

5. Advanced Features

// Search functionality searchTasks(query) { if (!query.trim()) { this.renderTasks(); return; } const searchResults = this.tasks.filter(task => task.title.toLowerCase().includes(query.toLowerCase()) || task.description.toLowerCase().includes(query.toLowerCase()) || task.category.toLowerCase().includes(query.toLowerCase()) ); this.renderFilteredTasks(searchResults); } // Task statistics updateTaskStats() { const totalTasks = this.tasks.length; const completedTasks = this.tasks.filter(task => task.completed).length; const overdueTasks = this.tasks.filter(task => task.isOverdue()).length; document.getElementById('total-tasks').textContent = totalTasks; document.getElementById('completed-tasks').textContent = completedTasks; document.getElementById('overdue-tasks').textContent = overdueTasks; } // Notification system showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.textContent = message; document.body.appendChild(notification); // Trigger animation setTimeout(() => notification.classList.add('show'), 100); // Remove notification after 3 seconds setTimeout(() => { notification.classList.remove('show'); setTimeout(() => notification.remove(), 300); }, 3000); } 
Enter fullscreen mode Exit fullscreen mode

🚀 Initialization and Usage

// Initialize the task manager when DOM is loaded document.addEventListener('DOMContentLoaded', () => { const taskManager = new TaskManager(); // Make it globally accessible for debugging window.taskManager = taskManager; console.log('Task Manager initialized successfully!'); }); 
Enter fullscreen mode Exit fullscreen mode

🎨 Key JavaScript Concepts Demonstrated

1. ES6+ Classes

We use modern class syntax to organize our code into reusable, maintainable components.

2. Array Methods

Extensive use of filter(), map(), find(), sort(), and other array methods for data manipulation.

3. Local Storage

Persistent data storage using the browser's localStorage API.

4. Event Delegation

Efficient event handling using event delegation for dynamic content.

5. Template Literals

Clean string interpolation and multi-line strings for HTML generation.

6. Destructuring and Spread Operator

Modern JavaScript syntax for cleaner, more readable code.

🔧 Performance Optimizations

  1. Event Delegation: Instead of adding listeners to each task, we use a single listener on the container.

  2. Efficient Rendering: Only re-render when necessary and use document fragments for multiple DOM insertions.

  3. Debounced Search: Implement debouncing for search functionality to improve performance.

// Debounced search implementation const debounce = (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Usage const debouncedSearch = debounce((query) => { this.searchTasks(query); }, 300); 
Enter fullscreen mode Exit fullscreen mode

🎯 What You've Learned

Building this task manager teaches you:

  • Object-Oriented Programming in JavaScript
  • DOM manipulation techniques
  • Event handling and delegation
  • Local storage for data persistence
  • Array methods for data processing
  • Modern JavaScript features (ES6+)
  • Performance optimization techniques

🚀 Next Steps

To extend this project, consider adding:

  1. Drag and drop for task reordering
  2. Task categories with color coding
  3. Export/import functionality
  4. Reminder notifications using the Notification API
  5. Collaborative features with real-time sync
  6. Progressive Web App capabilities

🎉 Conclusion

This task manager demonstrates the power of vanilla JavaScript for building interactive web applications. By understanding these core concepts, you'll be well-equipped to tackle more complex JavaScript projects and frameworks.

The complete source code is available on GitHub. Feel free to fork, modify, and make it your own!


What's your favorite JavaScript feature used in this project? Let me know in the comments below! 👇

Tags: #javascript #webdev #tutorial #programming #beginners

Top comments (0)