Project: a to-do list with JavaScript

A real to-do list: add, mark as done, delete and save in the browser with localStorage. The code runs right here.

What you will build

A to-do list that saves in your browser: you write a task, tick it when done and it stays there even if you close the tab. It combines the DOM, events, an array of objects and localStorage.

This is not a simulation: it is the page's real JavaScript, running. Add tasks and reload the page to see them persist.

Step 1: the minimal HTML

A form to type into, an empty list and a place for the counter. Nothing else.

<form id="todoForm">
    <input type="text" id="todoInput" placeholder="Nueva tarea…">
    <button type="submit">Añadir</button>
</form>
<ul id="todoList"></ul>
<p id="todoCount"></p>

Step 2: store the tasks in an array

Each task is an object { texto, hecha }. Together, an array. The array is the "source of truth": the screen just draws it.

let tareas = [];

document.getElementById("todoForm").addEventListener("submit", (e) => {
    e.preventDefault();                    // no recargar la página
    const input = document.getElementById("todoInput");
    const texto = input.value.trim();
    if (!texto) return;
    tareas.push({ texto: texto, hecha: false });
    input.value = "";
    render();
});

Step 3: draw the list (render)

We clear the list and rebuild it from the array. Each task produces an <li> with its checkbox and its delete button.

function render() {
    const ul = document.getElementById("todoList");
    ul.innerHTML = "";

    tareas.forEach((t, i) => {
        const li = document.createElement("li");
        if (t.hecha) li.classList.add("is-done");

        const chk = document.createElement("input");
        chk.type = "checkbox";
        chk.checked = t.hecha;
        chk.addEventListener("change", () => {
            tareas[i].hecha = chk.checked;
            render();
        });

        const span = document.createElement("span");
        span.textContent = t.texto;

        const del = document.createElement("button");
        del.textContent = "✕";
        del.addEventListener("click", () => {
            tareas.splice(i, 1);
            render();
        });

        li.append(chk, span, del);
        ul.appendChild(li);
    });

    const pendientes = tareas.filter(t => !t.hecha).length;
    document.getElementById("todoCount").textContent = "Quedan " + pendientes;
}

Step 4: don't lose it (localStorage)

localStorage only stores text, so we convert the array to JSON when saving and back when loading.

function guardar() {
    localStorage.setItem("tareas", JSON.stringify(tareas));
}

// al arrancar la página:
tareas = JSON.parse(localStorage.getItem("tareas")) || [];
render();

// y llamamos a guardar() después de cada cambio (añadir, marcar, borrar)

Where to go next

  • Edit a task with a double click.
  • Filters: all / pending / done.
  • Drag to reorder.
  • Swap localStorage for a real API with fetch.