Menu
×
   ❮     
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR ANGULARJS GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SWIFT SASS VUE GEN AI SCIPY AWS CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

Brtest Tutorial


Project: Interactive To-Do List

In this project, you will build a simple To-Do app where users can add tasks, mark them as done, and delete them. You will use arrays to store tasks, events to handle clicks, and DOM methods to update the list on the page.


Features

  • Add a new task
  • Mark a task as completed
  • Delete a task
  • Persist tasks for this session using JavaScript memory (array)

Starter UI

Example

Basic HTML and CSS for the app:

<style> body { font-family: Arial, sans-serif; background: #f7f7f7; margin: 0; } .app { max-width: 420px; margin: 24px auto; background: #fff; padding: 16px 16px 8px; border-radius: 10px; box-shadow: 0 6px 18px rgba(0,0,0,0.06); } h3 { margin: 0 0 12px; } .row { display: flex; gap: 8px; } #taskInput { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 6px; } #addBtn { padding: 10px 14px; background: #111; color: #fff; border: 0; border-radius: 6px; cursor: pointer; } #addBtn:hover { background: #333; } ul { list-style: none; padding: 0; margin: 12px 0 0; } li { display: grid; grid-template-columns: 24px 1fr auto; align-items: center; gap: 10px; padding: 10px; border: 1px solid #eee; border-radius: 8px; margin-bottom: 8px; background: #fafafa; } li.done .text { text-decoration: line-through; color: #888; } .del { background: #eee; border: 0; padding: 6px 10px; border-radius: 6px; cursor: pointer; } .del:hover { background: #e2e2e2; } .empty { color: #777; text-align: center; margin: 12px 0 4px; } </style> <div class='app'> <h3>To-Do List</h3> <div class='row'> <input id='taskInput' placeholder='Add a new task' aria-label='Task'> <button id='addBtn'>Add</button> </div> <p id='emptyMsg' class='empty'>No tasks yet</p> <ul id='list'></ul> </div>
Try it Yourself »

Core JavaScript

We will store tasks in an array of objects: { id, text, done }. Rendering will rebuild the list whenever the array changes.

Example

App logic: add, toggle done, delete, and render:

<script> // State let tasks = []; let nextId = 1; // Elements const input = document.getElementById('taskInput'); const addBtn = document.getElementById('addBtn'); const list = document.getElementById('list'); const emptyMsg = document.getElementById('emptyMsg'); // Render function function render() { list.innerHTML = ''; if (tasks.length === 0) { emptyMsg.style.display = 'block'; return; } emptyMsg.style.display = 'none'; for (const task of tasks) { const li = document.createElement('li'); if (task.done) li.classList.add('done'); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = task.done; checkbox.setAttribute('aria-label', 'Mark done'); checkbox.addEventListener('change', () => toggleDone(task.id)); const span = document.createElement('span'); span.className = 'text'; span.textContent = task.text; const delBtn = document.createElement('button'); delBtn.className = 'del'; delBtn.textContent = 'Delete'; delBtn.addEventListener('click', () => removeTask(task.id)); li.appendChild(checkbox); li.appendChild(span); li.appendChild(delBtn); list.appendChild(li); } } // Actions function addTask() { const text = input.value.trim(); if (text === '') return; tasks.push({ id: nextId++, text, done: false }); input.value = ''; render(); } function toggleDone(id) { const t = tasks.find(x => x.id === id); if (t) { t.done = !t.done; render(); } } function removeTask(id) { tasks = tasks.filter(x => x.id !== id); render(); } // Events addBtn.addEventListener('click', addTask); input.addEventListener('keydown', e => { if (e.key === 'Enter') addTask(); }); // Initial render(); </script>
Try it Yourself »

Optional: Save To Local Storage

Keep tasks between page reloads by saving to localStorage.

Example

Persist and load tasks:

<script> // Load saved tasks function load() { try { const raw = localStorage.getItem('todo_tasks'); const rawId = localStorage.getItem('todo_nextId'); tasks = raw ? JSON.parse(raw) : []; nextId = rawId ? Number(rawId) : 1; } catch { tasks = []; nextId = 1; } } function save() { localStorage.setItem('todo_tasks', JSON.stringify(tasks)); localStorage.setItem('todo_nextId', String(nextId)); } // Wrap render to also save const _render = render; render = function() { _render(); save(); }; // Load then render load(); render(); </script>
Try it Yourself »

Accessibility Tips

  • Use clear button text like 'Add' and 'Delete'.
  • Add aria-label to checkboxes and buttons when needed.
  • Ensure keyboard support: adding with Enter key, tabbing to buttons.

Challenges

  • Add an 'Edit' button that lets you modify a task's text inline.
  • Add a filter row: All, Active, Completed.
  • Show a counter: '3 items left'.
  • Prevent duplicate empty tasks and trim whitespace automatically.

Summary

  • Built a To-Do List using arrays, events, and DOM updates.
  • Implemented add, toggle, delete, and render functions.
  • Optionally persisted tasks using localStorage.

Next, you will learn how to work with JavaScript Strings and Numbers in more depth, including common methods and formatting.

Next » JavaScript Strings and Numbers

×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
sales@w3schools.com

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
help@w3schools.com

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookies and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.