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