← Back to Home

Stage 3: JavaScript Added

Core Concepts: Behavior

  • The Event Loop JS waits for things to happen. addEventListener tells the browser: "When a user submits this form, run my function."
  • DOM Manipulation JS can reach into the HTML structure, find elements, and change them dynamically. We can add new <li> elements on the fly.
  • Imperative Logic Notice how we have to write step-by-step instructions: "Create an li, create a span, set text, append to ul, update counter."
  • State (The Problem) The "state" (what tasks exist) is physically stored in the DOM. If we want to know how many tasks are done, we have to count the checked checkboxes.
Try adding a task. We finally have a working app! But as you add and delete items, notice how manually we have to keep the "tasks completed" text in sync with the actual list.
// Select elements from the DOM
const form = document.getElementById('todo-form');
const input = document.getElementById('todo-input');
const list = document.getElementById('todo-list');
const counter = document.getElementById('todo-counter');

// Imperative state syncing: manually read the DOM
function updateCounter() {
  const total = list.querySelectorAll('.todo-item').length;
  const done = list.querySelectorAll('.todo-checkbox:checked').length;
  counter.textContent = `${done} of ${total} tasks completed`;
}

// Event Listener for form submission
form.addEventListener('submit', function(e) {
  e.preventDefault(); // Prevent page refresh
  if (!input.value) return;

  // Imperatively build the DOM elements
  const li = document.createElement('li');
  li.className = 'todo-item';
  li.innerHTML = `
    <label class="todo-label">
      <input type="checkbox" class="todo-checkbox">
      <span class="todo-text">${input.value}</span>
    </label>
    <button class="btn btn-icon btn-delete">×</button>
  `;

  // Add delete logic
  const delBtn = li.querySelector('.btn-delete');
  delBtn.addEventListener('click', () => {
    li.remove();
    updateCounter();
  });

  // Add toggle logic
  const checkbox = li.querySelector('.todo-checkbox');
  checkbox.addEventListener('change', updateCounter);

  list.appendChild(li);
  input.value = '';
  updateCounter();
});

My Tasks

0 of 0 tasks completed