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

Darshil Mahraur @darshil89

About: SDE Intern @FUTR | Developer @hiremeClub | Aspiring DevOps Engineer | Ex-Intern @OSH & @Sumatak @Primebook

Location:
Bengaluru
Joined:
Apr 28, 2024

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

Publish Date: Jun 26
0 0

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

Comments 0 total

    Add comment