Final Mini Project: To-Do List Application0%

Final Mini Project: To-Do List Application

Beginner12 min read•Updated: Jul 11, 2026
Study Materials

Capstone Project: Production-Grade To-Do List Application

Congratulations on reaching the capstone project of the JavaScript for Beginners course! In this project, we combine all the fundamental concepts we've learned—variables, arrays, objects, functions, event handling, DOM manipulation, and localStorage—into a complete, commercial-grade To-Do List Web Application.


1. Application Features

Our Capstone To-Do Application includes:

  1. 1
    Task Management: Add tasks, delete tasks, and toggle completed status.
  2. 2
    Category Filtering: Filter tasks by All, Active, and Completed.
  3. 3
    Local Storage Persistence: Automatically serialize and persist task state across sessions.
  4. 4
    Task Counter & Cleanup: Show real-time active task counts and provide a "Clear Completed" button.
  5. 5
    Empty State: Beautiful UI when no tasks exist.

2. The HTML Structure (index.html)


3. CSS Styling (style.css)

CSS
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: system-ui, -apple-system, sans-serif;
}
 
body {
background: #0f172a;
color: #f8fafc;
min-height: 100vh;
display: flex;
justify-content: center;
padding: 3rem 1rem;
}
 
.app-container {
background: #1e293b;
width: 100%;
max-width: 520px;
border-radius: 1rem;
padding: 2rem;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5);
}
 
.app-header h1 {
font-size: 2rem;
color: #38bdf8;
margin-bottom: 0.25rem;
}
 
.app-header p {
font-size: 0.875rem;
color: #94a3b8;
margin-bottom: 1.5rem;
}
 
#todo-form {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
 
#todo-input {
flex: 1;
padding: 0.75rem 1rem;
background: #0f172a;
border: 1px solid #334155;
border-radius: 0.5rem;
color: #fff;
font-size: 1rem;
}
 
#todo-input:focus {
outline: none;
border-color: #38bdf8;
}
 
#add-btn {
background: #0284c7;
color: white;
border: none;
padding: 0.75rem 1.25rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
}
 
.filters {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
border-bottom: 1px solid #334155;
padding-bottom: 0.75rem;
}
 
.filter-btn {
background: transparent;
border: none;
color: #94a3b8;
padding: 0.25rem 0.5rem;
cursor: pointer;
font-weight: 500;
border-radius: 0.25rem;
}
 
.filter-btn.active {
color: #38bdf8;
background: rgba(56, 189, 248, 0.1);
}
 
.todo-list {
list-style: none;
max-height: 350px;
overflow-y: auto;
}
 
.todo-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem;
background: #0f172a;
border-radius: 0.5rem;
margin-bottom: 0.5rem;
}
 
.todo-item.completed span {
text-decoration: line-through;
color: #64748b;
}
 
.todo-text {
flex: 1;
margin: 0 0.75rem;
cursor: pointer;
}
 
.delete-btn {
background: transparent;
border: none;
color: #ef4444;
cursor: pointer;
font-size: 1.25rem;
}
 
.app-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid #334155;
font-size: 0.875rem;
color: #94a3b8;
}
 
.clear-btn {
background: transparent;
border: none;
color: #94a3b8;
cursor: pointer;
}
 
.clear-btn:hover { color: #f8fafc; }

4. The Complete JavaScript Application (app.js)

JavaScript
// 1. State Management
let todos = JSON.parse(localStorage.getItem("todos_data")) || [];
let currentFilter = "all";
 
// 2. Select DOM Elements
const todoForm = document.querySelector("#todo-form");
const todoInput = document.querySelector("#todo-input");
const todoList = document.querySelector("#todo-list");
const itemsLeftSpan = document.querySelector("#items-left");
const filterBtns = document.querySelectorAll(".filter-btn");
const clearCompletedBtn = document.querySelector("#clear-completed-btn");
 
// 3. Helper: Save State to LocalStorage
function saveTodos() {
localStorage.setItem("todos_data", JSON.stringify(todos));
}
 
// 4. Render Function: Renders UI based on Filter and State
function render() {
todoList.innerHTML = "";
 
// Filter tasks based on current tab
const filteredTodos = todos.filter((todo) => {
if (currentFilter === "active") return !todo.completed;
if (currentFilter === "completed") return todo.completed;
return true; // 'all'
});
 
if (filteredTodos.length === 0) {
todoList.innerHTML = `<li style="text-align: center; color: #64748b; padding: 1rem;">No tasks found</li>`;
}
 
filteredTodos.forEach((todo) => {
const li = document.createElement("li");
li.classList.add("todo-item");
if (todo.completed) li.classList.add("completed");
 
li.innerHTML = `
<input type="checkbox" class="toggle-check" ${todo.completed ? "checked" : ""}>
<span class="todo-text">${escapeHTML(todo.text)}</span>
<button class="delete-btn" aria-label="Delete Task">&times;</button>
`;
 
// Toggle Task Status
const checkbox = li.querySelector(".toggle-check");
checkbox.addEventListener("change", () => toggleTodo(todo.id));
 
// Delete Task
const deleteBtn = li.querySelector(".delete-btn");
deleteBtn.addEventListener("click", () => deleteTodo(todo.id));
 
todoList.appendChild(li);
});
 
// Update active count badge
const activeCount = todos.filter((t) => !t.completed).length;
itemsLeftSpan.textContent = `${activeCount} item${activeCount === 1 ? "" : "s"} left`;
}
 
// Security: Prevent XSS
function escapeHTML(str) {
const div = document.createElement("div");
div.textContent = str;
return div.innerHTML;
}
 
// 5. Actions: Add, Toggle, Delete, Clear
function addTodo(text) {
const newTodo = {
id: Date.now().toString(),
text: text.trim(),
completed: false
};
todos.push(newTodo);
saveTodos();
render();
}
 
function toggleTodo(id) {
todos = todos.map((todo) => {
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
}
return todo;
});
saveTodos();
render();
}
 
function deleteTodo(id) {
todos = todos.filter((todo) => todo.id !== id);
saveTodos();
render();
}
 
function clearCompleted() {
todos = todos.filter((todo) => !todo.completed);
saveTodos();
render();
}
 
// 6. Event Listeners
todoForm.addEventListener("submit", (e) => {
e.preventDefault();
const text = todoInput.value.trim();
if (text.length > 0) {
addTodo(text);
todoInput.value = "";
}
});
 
filterBtns.forEach((btn) => {
btn.addEventListener("click", () => {
filterBtns.forEach((b) => b.classList.remove("active"));
btn.classList.add("active");
currentFilter = btn.dataset.filter;
render();
});
});
 
clearCompletedBtn.addEventListener("click", clearCompleted);
 
// Initial Load
render();

Interactive Quiz

Practice Quiz

Question 1
How does JSON.stringify(todos) assist with localStorage persistence?
Question 2
What technique is used to generate unique IDs for each task item in Date.now().toString()?
Question 3
Why does todoForm.addEventListener("submit", ...) call e.preventDefault()?
Question 4
Which array method is used to filter out completed tasks when running clearCompleted()?
Question 5
What does the spread syntax { ...todo, completed: !todo.completed } accomplish inside toggleTodo?

Related Lessons

Previous LessonNext Lesson
Image Slider (Basic)None

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext