[JavaScript Series 4] Make the Page Interactive with DOM Selection and Events

한국어 버전

It is time to create real reactions on the page. The DOM (Document Object Model) is the tree the browser builds for the UI, and events represent user actions. We continue following concept → code → reason.

Key terms

  1. addEventListener: the method that registers the function to run when clicks, inputs, and other events occur.
  2. Event delegation: wiring one listener on a parent list and detecting which child was clicked, which scales better.
  3. IntersectionObserver: a watcher API that notifies you when an element enters the viewport, ideal for scroll animations.

Core ideas

  • DOM selectors: querySelector and querySelectorAll find elements. Because a match may not exist, pair them with optional chaining (?.).
  • Event handlers: functions that run when clicks, input, or scroll events happen. Register them with addEventListener.
  • classList API: use classList.add/remove/toggle to update CSS classes and visualize state.
  • Event delegation: attach one handler to the list container and check event.target to know which button fired. It automatically works for newly added items.
  • IntersectionObserver: a browser API that tracks whether elements are visible. It is lighter than running scroll listeners constantly and works well for scroll-driven effects.

Code examples

const toggleBtn = document.querySelector(".nav-toggle");
const menu = document.querySelector(".nav-menu");

toggleBtn?.addEventListener("click", () => {
  menu?.classList.toggle("is-open");
});

Navigation toggles are everywhere on mobile. classList.toggle flips between the open and closed states with one line.

const form = document.querySelector(".todo-form");
const list = document.querySelector(".todo-list");

form?.addEventListener("submit", (event) => {
  event.preventDefault();
  const input = form.querySelector("input[name=title]");
  const value = input?.value.trim();
  if (!value) return;

  const item = document.createElement("li");
  item.className = "todo-item";
  item.innerHTML = `${value} <button class="todo-remove">Remove</button>`;
  list?.appendChild(item);
  if (input) input.value = "";
});

preventDefault stops the form from reloading the page. Even in a single-page exercise you want the list to update immediately.

list?.addEventListener("click", (event) => {
  const target = event.target;
  if (!(target instanceof HTMLElement)) return;
  if (!target.matches(".todo-remove")) return;

  target.closest(".todo-item")?.remove();
});

Event delegation keeps every newly added delete button working without extra wiring.

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add("is-visible");
    }
  });
});

document.querySelectorAll(".feature").forEach((section) => {
  observer.observe(section);
});

IntersectionObserver tells you when a specific element enters the viewport, which means scroll-triggered animations without constantly checking scroll events.

Visualize DOM state changes with BrowserMock

If you want to inspect DOM state changes without drawing the UI for real, BrowserMock, a lightweight mock browser tool, lets you mount HTML snippets inside a fake browser.


const { document } = mock(`
  <button class="nav-toggle"></button>
  <nav class="nav-menu"></nav>
`);

const toggleBtn = document.querySelector(".nav-toggle");
const menu = document.querySelector(".nav-menu");

toggleBtn?.addEventListener("click", () => {
  menu?.classList.toggle("is-open");
});

toggleBtn?.dispatchEvent(new document.defaultView.Event("click"));
console.log(menu?.className); // "nav-menu is-open"

BrowserMock prints the DOM selection → event → state change order. You can confirm classList transitions during the sketch phase before touching production markup.

Why it matters

  • Sites such as student council portals rely on snappy interactions, so DOM selection and event patterns directly shape the user experience.
  • Reading input and refreshing the list immediately gives feedback without a server round trip.
  • Event delegation and IntersectionObserver protect performance when the number of elements grows, keeping mobile devices smooth.

Practice

  • Follow along: prepare todo-form, todo-list, and nav-toggle elements, then connect add, delete, and toggle interactions.
  • Extend: save data-priority on each todo, change classes based on priority when a button is pressed, and add scroll animations via IntersectionObserver.
  • Debug: intentionally break selectors so they return null, add optional chaining to guard them, and log the difference between event.target and event.currentTarget.
  • Done when: a single HTML page can add, remove, toggle, and animate regardless of how many items you append.

Wrap-up

Once you master DOM selection and event flow you can build most UI interactions yourself. Next up we will connect these patterns to fetch and async/await so data from the server flows into the screen.

💬 댓글

이 글에 대한 의견을 남겨주세요