A to-do list with add, complete, and delete — all persisted in the browser via localStorage.
A simple form and an empty list.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>To-Do</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<?php require_once __DIR__ . '/../promo_banner.php'; ?>
<div class="app">
<h1>✅ To-Do</h1>
<form id="todo-form">
<input id="todo-input" type="text" placeholder="Add a task..." required>
<button type="submit">Add</button>
</form>
<ul id="todo-list"></ul>
</div>
<script src="todo.js"></script>
</body>
</html>
Load tasks on start, save on every change.
let todos = JSON.parse(localStorage.getItem('todos')) || [];
function save() { localStorage.setItem('todos', JSON.stringify(todos)); }
function render() {
document.getElementById('todo-list').innerHTML = todos.map((t, i) => `
<li class="${t.done ? 'done' : ''}">
<span onclick="toggle(${i})">${t.done ? '☑' : '☐'} ${t.text}</span>
<button onclick="remove(${i})">✕</button>
</li>`).join('');
}
function toggle(i) { todos[i].done = !todos[i].done; save(); render(); }
function remove(i) { todos.splice(i, 1); save(); render(); }
document.getElementById('todo-form').addEventListener('submit', e => {
e.preventDefault();
const text = document.getElementById('todo-input').value.trim();
if (!text) return;
todos.push({ text, done: false });
save(); render();
document.getElementById('todo-input').value = '';
});
render();
Your to-do app now remembers tasks between sessions. A small but genuinely useful app to add to your portfolio.
Spotted a bug, broken code, or something that doesn't look right? Tell us what's off and we'll fix it.