Skip to content

Simple Todo List

Medium~30 min

What you'll build

A page with an input, an Add button, and a list. Tasks can be checked off and removed. The list is re-rendered from an array on every change.

Goals

  • Keep todo state in an array of objects
  • Render the list from state on every change
  • Add, toggle, and delete items via DOM events
  • Use unique ids to identify items in the DOM

Prerequisites

  • Arrays and array methods (push, filter, map)
  • Objects and property access
  • DOM selection and event listeners

Steps

  1. 1

    Set up state and HTML

    Create an input, an Add button, and an empty ul. In JS, declare a todos array and a nextId counter. State drives everything; the DOM is just a view of it.

    Code
    <input id="task-input" placeholder="Add a task">
    <button id="add-btn">Add</button>
    <ul id="list"></ul>
    
    <script>
      let todos = [];
      let nextId = 1;
    </script>

    Expected result

    An input, a button, and an empty list area appear.

  2. 2

    Write the render function

    Write render() that clears the ul and rebuilds it from the todos array. Each li gets a checkbox, the title, and a delete button, plus a data-id attribute to identify it.

    Code
    const listEl = document.getElementById("list");
    
    function render() {
      listEl.innerHTML = "";
      todos.forEach(t => {
        const li = document.createElement("li");
        li.innerHTML = `
          <input type="checkbox" ${t.done ? "checked" : ""} data-id="${t.id}">
          <span style="text-decoration:${t.done ? "line-through" : "none"}">${t.title}</span>
          <button data-id="${t.id}" class="del">x</button>
        `;
        listEl.appendChild(li);
      });
    }

    Expected result

    Calling render() draws the current todos; an empty array shows nothing.

  3. 3

    Wire the Add button

    On Add click, read the input value, push a new task object to todos, increment nextId, clear the input, and call render(). Empty input is ignored.

    Code
    const inputEl = document.getElementById("task-input");
    const addBtn = document.getElementById("add-btn");
    
    addBtn.addEventListener("click", () => {
      const title = inputEl.value.trim();
      if (!title) return;
      todos.push({ id: nextId++, title, done: false });
      inputEl.value = "";
      render();
    });

    Expected result

    Typing a task and clicking Add adds it to the list and clears the input.

  4. 4

    Handle toggle and delete with delegation

    Add one click listener on the ul. If the clicked element is a checkbox, toggle that task's done flag. If it is a delete button, filter the task out. Then re-render. Event delegation handles all current and future items with one listener.

    Code
    listEl.addEventListener("click", (e) => {
      const id = Number(e.target.dataset.id);
      if (e.target.type === "checkbox") {
        const t = todos.find(t => t.id === id);
        if (t) t.done = e.target.checked;
      } else if (e.target.classList.contains("del")) {
        todos = todos.filter(t => t.id !== id);
      }
      render();
    });

    Expected result

    Checking a box strikes through the task; clicking x removes it.

Try it yourself

What you learned

You built a complete CRUD todo list using state-driven rendering and event delegation — two patterns that scale to real applications. The same architecture runs behind most interactive UIs.

Related Resources

Related Lessons

  • Arrays

    push, filter, find, and forEach drive the todo state.

  • DOM

    Event delegation on the ul handles all current and future items.

  • Objects

    Each todo is an object with id, title, and done.

Related Practice

Related Tools

  • JSON Formatter

    Inspect the todos array as JSON — exactly how it would be persisted.

Related Cheatsheets

Related Reference

Related Errors

Related Glossary

Related Glossary

A beginner-friendly glossary explains jargon in plain English — variables, functions, DOM, Promise, and more.

Related Errors

Plain-English explanations of common errors — what they mean, why they happen, and how to fix them.

← Back to topic