Build a Complete Interactive App
It's time to apply everything you've learned! This final project combines HTML structure, CSS styling, and JavaScript functionality to create a real, production-ready application.
You'll implement features like adding tasks, marking them complete, filtering, local storage persistence, and responsive design.
This project demonstrates how all JavaScript concepts work together: DOM manipulation, events, arrays, objects, local storage, and more.
Example
// Complete To-Do Application JavaScript
class TodoApp {
constructor() {
this.todos = this.loadTodos();
this.filter = 'all';
this.init();
}
init() {
this.renderTodos();
this.attachEventListeners();
}
attachEventListeners() {
const form = document.getElementById('todoForm');
const input = document.getElementById('todoInput');
const filterBtns = document.querySelectorAll('.filter-btn');
form.addEventListener('submit', (e) => {
e.preventDefault();
this.addTodo(input.value.trim());
input.value = '';
});
filterBtns.forEach(btn => {
btn.addEventListener('click', (e) => {
this.setFilter(e.target.dataset.filter);
});
});
}
addTodo(text) {
if (!text) return;
const todo = {
id: Date.now(),
text: text,
completed: false,
createdAt: new Date().toISOString()
};
this.todos.push(todo);
this.saveTodos();
this.renderTodos();
}
toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
this.saveTodos();
this.renderTodos();
}
}
deleteTodo(id) {
this.todos = this.todos.filter(t => t.id !== id);
this.saveTodos();
this.renderTodos();
}
setFilter(filter) {
this.filter = filter;
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.toggle('active',
btn.dataset.filter === filter);
});
this.renderTodos();
}
getFilteredTodos() {
switch(this.filter) {
case 'active':
return this.todos.filter(t => !t.completed);
case 'completed':
return this.todos.filter(t => t.completed);
default:
return this.todos;
}
}
renderTodos() {
const container = document.getElementById('todoList');
const filtered = this.getFilteredTodos();
if (filtered.length === 0) {
container.innerHTML =
'<p class="empty">No tasks found</p>';
return;
}
container.innerHTML = filtered.map(todo => `
<div class="todo-item ${todo.completed ? 'completed' : ''}">
<input type="checkbox"
${todo.completed ? 'checked' : ''}
onchange="app.toggleTodo(${todo.id})">
<span>${this.escapeHtml(todo.text)}</span>
<button onclick="app.deleteTodo(${todo.id})">
Delete
</button>
</div>
`).join('');
this.updateStats();
}
updateStats() {
const total = this.todos.length;
const completed = this.todos.filter(t => t.completed).length;
const active = total - completed;
document.getElementById('stats').innerHTML = `
Total: ${total} | Active: ${active} | Completed: ${completed}
`;
}
saveTodos() {
localStorage.setItem('todos', JSON.stringify(this.todos));
}
loadTodos() {
const saved = localStorage.getItem('todos');
return saved ? JSON.parse(saved) : [];
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// Initialize app
const app = new TodoApp(); - Class-Based Structure - Organized code using ES6 classes
- Local Storage - Persist todos across browser sessions
- CRUD Operations - Create, Read, Update, Delete tasks
- Filtering - Show all, active, or completed tasks
- Event Handling - Form submission, clicks, checkboxes
- DOM Manipulation - Dynamic rendering of task list
- Data Validation - Input sanitization and validation
- Statistics - Real-time count of tasks
- Responsive Design - Mobile-friendly interface
Complete To-Do Application
HTML
<div class="todo-app">
<h1>๐ My To-Do List</h1>
<form id="todoForm">
<input
type="text"
id="todoInput"
placeholder="What needs to be done?"
required
autocomplete="off">
<button type="submit">Add Task</button>
</form>
<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>
<div id="todoList"></div>
<div id="stats" class="stats">Total: 0 | Active: 0 | Completed: 0</div>
<div class="footer">
<button id="clearCompleted">Clear Completed</button>
<button id="clearAll">Clear All</button>
</div>
</div> CSS
.todo-app {
max-width: 600px;
margin: 0 auto;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
#todoForm {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
#todoInput {
flex: 1;
padding: 12px;
font-size: 16px;
border: 2px solid #ddd;
border-radius: 8px;
outline: none;
}
#todoInput:focus {
border-color: #2196F3;
}
button[type="submit"] {
padding: 12px 24px;
background: #2196F3;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
button[type="submit"]:hover {
background: #1976D2;
}
.filters {
display: flex;
gap: 10px;
margin-bottom: 20px;
justify-content: center;
}
.filter-btn {
padding: 8px 16px;
background: white;
border: 2px solid #ddd;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
.filter-btn:hover {
border-color: #2196F3;
}
.filter-btn.active {
background: #2196F3;
color: white;
border-color: #2196F3;
}
#todoList {
background: white;
border-radius: 8px;
min-height: 200px;
margin-bottom: 20px;
}
.todo-item {
display: flex;
align-items: center;
padding: 15px;
border-bottom: 1px solid #eee;
gap: 12px;
transition: background 0.2s;
}
.todo-item:hover {
background: #f5f5f5;
}
.todo-item input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
}
.todo-item span {
flex: 1;
font-size: 16px;
transition: all 0.3s;
}
.todo-item.completed span {
text-decoration: line-through;
color: #999;
opacity: 0.6;
}
.todo-item button {
padding: 6px 12px;
background: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: background 0.3s;
}
.todo-item button:hover {
background: #d32f2f;
}
.empty {
text-align: center;
padding: 40px;
color: #999;
font-style: italic;
}
.stats {
text-align: center;
padding: 15px;
background: #e3f2fd;
border-radius: 8px;
color: #1976D2;
font-weight: bold;
margin-bottom: 20px;
}
.footer {
display: flex;
gap: 10px;
justify-content: center;
}
.footer button {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
#clearCompleted {
background: #ff9800;
color: white;
}
#clearCompleted:hover {
background: #f57c00;
}
#clearAll {
background: #f44336;
color: white;
}
#clearAll:hover {
background: #d32f2f;
} JavaScript
// Complete To-Do App Implementation
class TodoApp {
constructor() {
this.todos = this.loadTodos();
this.filter = 'all';
this.init();
}
init() {
this.renderTodos();
this.attachEventListeners();
}
attachEventListeners() {
document.getElementById('todoForm').addEventListener('submit', (e) => {
e.preventDefault();
const input = document.getElementById('todoInput');
this.addTodo(input.value.trim());
input.value = '';
});
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.setFilter(e.target.dataset.filter);
});
});
document.getElementById('clearCompleted').addEventListener('click', () => {
this.clearCompleted();
});
document.getElementById('clearAll').addEventListener('click', () => {
if (confirm('Delete all tasks?')) {
this.clearAll();
}
});
}
addTodo(text) {
if (!text) return;
const todo = {
id: Date.now(),
text: text,
completed: false,
createdAt: new Date().toISOString()
};
this.todos.push(todo);
this.saveTodos();
this.renderTodos();
}
toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
this.saveTodos();
this.renderTodos();
}
}
deleteTodo(id) {
this.todos = this.todos.filter(t => t.id !== id);
this.saveTodos();
this.renderTodos();
}
clearCompleted() {
this.todos = this.todos.filter(t => !t.completed);
this.saveTodos();
this.renderTodos();
}
clearAll() {
this.todos = [];
this.saveTodos();
this.renderTodos();
}
setFilter(filter) {
this.filter = filter;
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.filter === filter);
});
this.renderTodos();
}
getFilteredTodos() {
switch(this.filter) {
case 'active':
return this.todos.filter(t => !t.completed);
case 'completed':
return this.todos.filter(t => t.completed);
default:
return this.todos;
}
}
renderTodos() {
const container = document.getElementById('todoList');
const filtered = this.getFilteredTodos();
if (filtered.length === 0) {
container.innerHTML = '<p class="empty">No tasks to show</p>';
} else {
container.innerHTML = filtered.map(todo => `
<div class="todo-item ${todo.completed ? 'completed' : ''}">
<input type="checkbox"
${todo.completed ? 'checked' : ''}
onchange="app.toggleTodo(${todo.id})">
<span>${this.escapeHtml(todo.text)}</span>
<button onclick="app.deleteTodo(${todo.id})">Delete</button>
</div>
`).join('');
}
this.updateStats();
}
updateStats() {
const total = this.todos.length;
const completed = this.todos.filter(t => t.completed).length;
const active = total - completed;
document.getElementById('stats').innerHTML =
`Total: ${total} | Active: ${active} | Completed: ${completed}`;
}
saveTodos() {
localStorage.setItem('todos', JSON.stringify(this.todos));
}
loadTodos() {
try {
const saved = localStorage.getItem('todos');
return saved ? JSON.parse(saved) : [];
} catch (error) {
console.error('Error loading todos:', error);
return [];
}
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// Initialize the app
const app = new TodoApp(); Notes
- Congratulations! You've completed the JavaScript course and built a real application! You now have the skills to create interactive web applications.
- Next Steps: Build more projects! Try a weather app with APIs, a calculator, a quiz game, or anything that interests you. Practice is key!
- Keep Learning: Explore frameworks like React, Vue, or Angular. Learn Node.js for backend. Join developer communities and keep coding!
