Learn to create a fully functional task management app with JavaScript, localStorage, and filtering!
No tasks yet. Add your first task above!
Create new tasks dynamically
Update task text inline
Remove completed tasks
Toggle task completion
View all, active, or completed
Save tasks with localStorage
Create the input field, add button, filters, and task list container:
<div class="todo-app">
<div class="todo-header">
<input type="text" id="taskInput" placeholder="Enter task..." />
<button id="addBtn">Add Task</button>
</div>
<div class="filters">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="active">Active</button>
<button class="filter-btn" data-filter="completed">Completed</button>
</div>
<ul class="task-list" id="taskList"></ul>
</div>
Set up the tasks array and load from localStorage:
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
let currentFilter = 'all';
const taskInput = document.getElementById('taskInput');
const addBtn = document.getElementById('addBtn');
const taskList = document.getElementById('taskList');
// Load tasks on page load
renderTasks();
Create function to add new tasks with unique IDs:
function addTask() {
const taskText = taskInput.value.trim();
if (taskText === '') return;
const task = {
id: Date.now(),
text: taskText,
completed: false
};
tasks.push(task);
saveTasks();
renderTasks();
taskInput.value = '';
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addTask();
});
Display tasks with filtering and event handlers:
function renderTasks() {
const filteredTasks = tasks.filter(task => {
if (currentFilter === 'active') return !task.completed;
if (currentFilter === 'completed') return task.completed;
return true;
});
if (filteredTasks.length === 0) {
taskList.innerHTML = '<div class="empty-state">No tasks!</div>';
return;
}
taskList.innerHTML = filteredTasks.map(task => `
<li class="task-item ${task.completed ? 'completed' : ''}">
<input type="checkbox"
class="task-checkbox"
${task.completed ? 'checked' : ''}
onchange="toggleTask(${task.id})">
<span class="task-text">${task.text}</span>
<div class="task-actions">
<button class="edit-btn" onclick="editTask(${task.id})">Edit</button>
<button class="delete-btn" onclick="deleteTask(${task.id})">Delete</button>
</div>
</li>
`).join('');
}
Implement toggle, edit, delete, and filter functions:
function toggleTask(id) {
tasks = tasks.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
);
saveTasks();
renderTasks();
}
function editTask(id) {
const task = tasks.find(t => t.id === id);
const newText = prompt('Edit task:', task.text);
if (newText !== null && newText.trim() !== '') {
tasks = tasks.map(t =>
t.id === id ? { ...t, text: newText.trim() } : t
);
saveTasks();
renderTasks();
}
}
function deleteTask(id) {
tasks = tasks.filter(task => task.id !== id);
saveTasks();
renderTasks();
}
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
// Filter buttons
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.filter-btn').forEach(b =>
b.classList.remove('active')
);
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderTasks();
});
});
Copy and paste this complete code into a new HTML file:
<!DOCTYPE html>
<html>
<head>
<title>To-Do List App</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; background: #f1f5f9; padding: 2rem; }
.todo-app { max-width: 600px; margin: 0 auto; background: white; padding: 2rem; border-radius: 15px; }
.todo-header { display: flex; gap: 1rem; margin-bottom: 2rem; }
#taskInput { flex: 1; padding: 1rem; border: 2px solid #e2e8f0; border-radius: 10px; font-size: 1rem; }
#addBtn { padding: 1rem 2rem; background: linear-gradient(135deg, #a8edea, #fed6e3); border: none; border-radius: 10px; font-weight: 600; cursor: pointer; }
.filters { display: flex; gap: 1rem; margin-bottom: 1.5rem; justify-content: center; }
.filter-btn { padding: 0.5rem 1.5rem; background: white; border: 2px solid #e2e8f0; border-radius: 50px; cursor: pointer; }
.filter-btn.active { background: linear-gradient(135deg, #a8edea, #fed6e3); }
.task-list { list-style: none; }
.task-item { background: #f1f5f9; padding: 1rem; border-radius: 10px; margin-bottom: 0.75rem; display: flex; align-items: center; gap: 1rem; }
.task-item.completed .task-text { text-decoration: line-through; opacity: 0.6; }
.task-text { flex: 1; }
.task-actions { display: flex; gap: 0.5rem; }
.edit-btn, .delete-btn { padding: 0.5rem 1rem; border: none; border-radius: 5px; cursor: pointer; color: white; }
.edit-btn { background: #3b82f6; }
.delete-btn { background: #ef4444; }
</style>
</head>
<body>
<div class="todo-app">
<h1 style="text-align:center; margin-bottom:2rem;">My To-Do List</h1>
<div class="todo-header">
<input type="text" id="taskInput" placeholder="Enter a new task..." />
<button id="addBtn">Add Task</button>
</div>
<div class="filters">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="active">Active</button>
<button class="filter-btn" data-filter="completed">Completed</button>
</div>
<ul class="task-list" id="taskList"></ul>
</div>
<script>
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
let currentFilter = 'all';
const taskInput = document.getElementById('taskInput');
const addBtn = document.getElementById('addBtn');
const taskList = document.getElementById('taskList');
function addTask() {
const taskText = taskInput.value.trim();
if (taskText === '') return;
tasks.push({ id: Date.now(), text: taskText, completed: false });
saveTasks();
renderTasks();
taskInput.value = '';
}
function renderTasks() {
const filtered = tasks.filter(t => {
if (currentFilter === 'active') return !t.completed;
if (currentFilter === 'completed') return t.completed;
return true;
});
taskList.innerHTML = filtered.length === 0 ? '<p style="text-align:center;padding:2rem;color:#64748b;">No tasks</p>' :
filtered.map(t => `
<li class="task-item ${t.completed ? 'completed' : ''}">
<input type="checkbox" ${t.completed ? 'checked' : ''} onchange="toggleTask(${t.id})">
<span class="task-text">${t.text}</span>
<div class="task-actions">
<button class="edit-btn" onclick="editTask(${t.id})">Edit</button>
<button class="delete-btn" onclick="deleteTask(${t.id})">Delete</button>
</div>
</li>
`).join('');
}
function toggleTask(id) {
tasks = tasks.map(t => t.id === id ? { ...t, completed: !t.completed } : t);
saveTasks();
renderTasks();
}
function editTask(id) {
const task = tasks.find(t => t.id === id);
const newText = prompt('Edit task:', task.text);
if (newText && newText.trim()) {
tasks = tasks.map(t => t.id === id ? { ...t, text: newText.trim() } : t);
saveTasks();
renderTasks();
}
}
function deleteTask(id) {
tasks = tasks.filter(t => t.id !== id);
saveTasks();
renderTasks();
}
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keypress', e => { if (e.key === 'Enter') addTask(); });
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderTasks();
});
});
renderTasks();
</script>
</body>
</html>