Before this lesson

Understand functions, arrays and HTML attributes. Prepare a separate practice page.

What you will be able to do

Update state from events and render from state rather than treating visible text as the data source.

Understand the mechanism

querySelector returns the first match or null. Put scripts after markup or use defer so elements exist when queried. The DOM is the current object tree, not the initial HTML string; editing it does not write server files.

Events move through capture, target and bubbling phases. target identifies the origin and currentTarget the listener owner. Delegation attaches a listener to a stable parent, finds an action with closest, and verifies container membership; it works for dynamic lists.

Write user content with textContent instead of interpolating innerHTML. Buttons provide keyboard behavior; listening for form submit also covers Enter. preventDefault cancels default behavior while stopPropagation affects propagation; they are not interchangeable.

Runnable example

<button id="counter" type="button">Count: 0</button>
<script>
  const button = document.querySelector("#counter");
  let count = 0;
  button.addEventListener("click", () => {
    count += 1;
    button.textContent = "Count: " + count;
  });
</script>

Trace and verify

Read the initial counter state, click three times and confirm one increment per click. Focus the button with Tab and press Enter for equivalent behavior. Refresh resets state because this example has no persistence.

Common mistakes and boundaries

Repeated initialization can attach duplicate listeners. Removing a listener requires the original function reference, not an identical new anonymous function. Disabled buttons do not enforce server permissions.

Practice challenge

Add decrement and reset buttons, keeping the count nonnegative. Centralize DOM updates in one render function.

Reference solution and reasoning

Handlers update state and call render. Decrement uses Math.max(0, count - 1). render updates both text and the decrement button disabled state so display logic stays consistent.

Further reading