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
💻 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)); } }
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; } }
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; }
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); } }); }
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); }
🚀 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!'); });
🎨 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
Event Delegation: Instead of adding listeners to each task, we use a single listener on the container.
Efficient Rendering: Only re-render when necessary and use document fragments for multiple DOM insertions.
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);
🎯 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:
- Drag and drop for task reordering
- Task categories with color coding
- Export/import functionality
- Reminder notifications using the Notification API
- Collaborative features with real-time sync
- 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)