JavaScript: Capstone Project: Todo App

Congratulations on reaching the final lesson! Over the past 27 lessons you've learned about variables, functions, arrays, the DOM, events, JSON, async programming, classes, and more. Now it's time to tie it all together and build a complete small application. This project will be the culmination of everything you've learned.


1. Project Overview

We'll build a Todo App — it may look simple, but it touches nearly every core concept you've studied. When you're done, you'll have a real, usable productivity tool.



2. Feature Requirements

  1. Add todo items — input field + button; pressing Enter or clicking adds a new item
  2. Toggle complete/incomplete — click a todo to toggle its done state (strikethrough effect)
  3. Delete todo items — each item has a delete button beside it
  4. Filtering — three buttons: All / Active / Completed
  5. Local storage — use localStorage to persist data across page refreshes
  6. Count remaining items — display "X items left" at the bottom


3. Technical Requirements

Concept Where It's Used
Class TodoApp class organizes all logic
async/await Wrap localStorage read/write as async functions
DOM manipulation Dynamically create list items, update content, remove elements
Event handling Form submit, list click, filter button click
JSON JSON.stringify to save, JSON.parse to load
Array methods filter, map, find to manage todo data
Template literals Build HTML strings


4. Acceptance Criteria



5. Reference Implementation Ideas

Below are hints and directions — not complete code. Writing it yourself is where the real learning happens.

(1) Data Structure

HTML
<script>
// Each todo item is an object
const todo = {
  id: Date.now(),      // unique identifier
  text: 'Learn JavaScript',
  completed: false
};
console.log(todo);

// All todo items are stored in an array
let todos = [];
</script>

(2) Class Structure

HTML
<script>
class TodoApp {
  constructor() {
    this.todos = [];         // data
    this.filter = 'all';     // current filter
    this.init();             // initialize
  }

  async init() {
    // 1. Load data from localStorage
    // 2. Bind events
    // 3. Render the list
  }

  async save() {
    // JSON.stringify → localStorage.setItem
  }

  async load() {
    // localStorage.getItem → JSON.parse
  }

  addTodo(text) { /* create object, push, save, render */ }
  toggleTodo(id) { /* find the item, flip completed, save, render */ }
  deleteTodo(id) { /* filter out the item, save, render */ }

  render() {
    // 1. Filter todos based on this.filter
    // 2. Clear the list container
    // 3. Loop through filtered array, create DOM nodes
    // 4. Update the count display
  }
}
</script>

(3) Event Binding Strategy

(4) Rendering Strategy

HTML
<script>
render() {
  const filtered = this.todos.filter(function(todo) {
    if (this.filter === 'active') return !todo.completed;
    if (this.filter === 'completed') return todo.completed;
    return true;  // 'all'
  }.bind(this));

  // Clear the container
  this.listEl.innerHTML = '';

  filtered.forEach(function(todo) {
    var li = document.createElement('li');
    // Set content, styles, data attributes
    // Bind toggle and delete events
    this.listEl.appendChild(li);
  }.bind(this));

  // Update the count
  var activeCount = this.todos.filter(function(t) { return !t.completed; }).length;
  this.countEl.textContent = activeCount + ' items left';
}
</script>


6. Extension Ideas

Once the basic features are working, try these advanced challenges:



7. Assignment

Complete the todo app above with all features, and pass all acceptance criteria.

Suggested steps:

  1. Start with the HTML structure and CSS styling
  2. Build the TodoApp class skeleton (constructor, init)
  3. Implement addTodo and render — get adding to work first
  4. Implement toggleTodo — toggle complete/incomplete
  5. Implement deleteTodo — remove items
  6. Implement filtering
  7. Implement save and load — persist data
  8. Show the remaining item count
  9. Final polish: empty input validation, style tweaks
💡 Tip: Don't try to build everything at once. Get the smallest thing working first, then add one piece at a time. Software development is "make it work, make it right, make it fast."


▶ Example

A complete Todo app using classes and localStorage:

HTML
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; max-width: 600px; margin: 20px auto; }
    .done { text-decoration: line-through; color: gray; }
    input, button { padding: 6px 12px; margin: 2px; }
  </style>
</head>
<body>
  <h1>Todo App</h1>
  <input id="taskInput" placeholder="New task">
  <button id="addBtn">Add</button>
  <ul id="taskList"></ul>
  <script>
    class Todo {
      constructor() { this.tasks = JSON.parse(localStorage.getItem('tasks') || '[]'); }
      save() { localStorage.setItem('tasks', JSON.stringify(this.tasks)); }
      add(text) { this.tasks.push({text, done: false}); this.save(); this.render(); }
      toggle(i) { this.tasks[i].done = !this.tasks[i].done; this.save(); this.render(); }
      render() {
        document.getElementById('taskList').innerHTML = this.tasks.map((t, i) =>
          `<li class="${t.done ? 'done' : ''}"><input type="checkbox" ${t.done ? 'checked' : ''} onclick="todo.toggle(${i})"> ${t.text}</li>`
        ).join('');
      }
    }
    const todo = new Todo();
    document.getElementById('addBtn').onclick = () => {
      const text = document.getElementById('taskInput').value.trim();
      if (text) { todo.add(text); document.getElementById('taskInput').value = ''; }
    };
    todo.render();
  </script>
</body>
</html>
▶ Try it Yourself

This demonstrates a complete Todo app with class-based state management, localStorage persistence, and dynamic DOM rendering.

▶ Example

Adding and toggling todo items with array methods:

JAVASCRIPT
let todos = [];

function addTodo(text) {
  todos.push({ id: Date.now(), text, completed: false });
}

function toggleTodo(id) {
  const todo = todos.find(t => t.id === id);
  if (todo) todo.completed = !todo.completed;
}

addTodo('Learn JavaScript');
addTodo('Build a project');
toggleTodo(todos[0].id);

console.log(todos);
// [{ id: ..., text: 'Learn JavaScript', completed: true },
//  { id: ..., text: 'Build a project', completed: false }]
▶ Try it Yourself

▶ Example

Filtering todos by completion state:

JAVASCRIPT
const todos = [
  { text: 'Learn HTML', completed: true },
  { text: 'Learn CSS', completed: true },
  { text: 'Learn JavaScript', completed: false }
];

const active = todos.filter(t => !t.completed);
const completed = todos.filter(t => t.completed);
const remaining = active.length;

console.log('Active:', active.map(t => t.text));
console.log('Completed:', completed.map(t => t.text));
console.log(remaining + ' item(s) left');
▶ Try it Yourself

❓ FAQ

Q Why use Class instead of a constructor function?
A Class syntax is cleaner — constructor, this, and inheritance all have clear syntax. Under the hood it's still prototypes, but it reads like OOP languages and is easier to maintain.
Q Will localStorage data be lost?
A No, localStorage persists until the user clears browser data or uses private browsing. Remember it only stores strings — objects need JSON.stringify before saving.
Q What is event delegation and why is it useful in a todo app?
A Event delegation means binding a listener to a parent element and using e.target to handle child events. New todo items automatically get event handling without rebinding — better performance.

📖 Summary

📝 Exercises

  1. Basic: Complete the core todo features — add, toggle complete, delete — using an array for storage (no localStorage needed).
  2. Intermediate: Add localStorage persistence and filtering (All / Active / Completed) to the basic version; data should survive a page refresh.
  3. Challenge: Implement at least one extension feature (drag & drop sorting, editing, category tags, theme switching, or due date reminders), and write a brief explanation of your approach.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏