Learn / Programming / JavaScript / The DOM and Events

JavaScript · Lesson 6 of 6

The DOM and Events

Select elements, react to clicks and update the page.

  • Intermediate
  • 14 min read
  • 3 objectives

Before this lessonLesson 5: Promises and async/await

What you will learn

  • Query the DOM
  • Add event listeners
  • Update text and classes

When a browser loads an HTML page it builds the DOM (Document Object Model), a tree of objects representing every element. JavaScript can read and change that tree, which is how pages become interactive. Everything starts from the global document object.

What the DOM is

When a browser loads HTML it builds a live tree of objects in memory, one for every element. That tree is the DOM (Document Object Model). JavaScript can find any node in the tree, read it, change it, add new ones, and react when the user interacts with them. Everything dynamic on a web page, from a dropdown menu to a live search, is JavaScript editing the DOM.

Picture the HTML as a family tree: <html> is the ancestor, <body> a child, and each paragraph or button a leaf further down. To change the page you locate the right relative, then change it.

<body>
  <h1 id="title">Todo</h1>
  <ul id="list">
    <li>Learn JS</li>
  </ul>
</body>

Selecting elements

const title = document.querySelector("h1");        // first match
const items = document.querySelectorAll(".item");  // all matches
const byId  = document.getElementById("save");

querySelector takes any CSS selector, so you already know the syntax. It returns null if nothing matches, so check before using the result.

Changing content and style

title.textContent = "Welcome back";
title.classList.add("highlight");
title.classList.toggle("hidden");
title.setAttribute("data-state", "ready");
title.style.color = "tomato";

Prefer textContent over innerHTML for plain text. Setting innerHTML with user-supplied text opens the door to cross-site scripting (XSS) attacks. Prefer toggling CSS classes over setting inline styles.

Events

An event listener runs a function when something happens: a click, a key press, a form submit. The function receives an event object describing what occurred.

const button = document.querySelector("#save");
let clicks = 0;
button.addEventListener("click", (event) => {
  clicks += 1;
  button.textContent = `Saved ${clicks} times`;
});

const form = document.querySelector("form");
form.addEventListener("submit", (e) => {
  e.preventDefault();               // stop the page reload
  console.log(new FormData(form).get("email"));
});

Creating elements

const li = document.createElement("li");
li.textContent = "New task";
document.querySelector("ul").append(li);

Event delegation

Instead of attaching a listener to every list item, attach one to the parent and inspect event.target. It works for items added later, and uses less memory.

document.querySelector("ul").addEventListener("click", (e) => {
  const li = e.target.closest("li");
  if (li) li.classList.toggle("done");
});

A complete example: a working to-do list

This page combines everything from the lesson: find elements, listen for a click, read an input, create an element and add it to the page. Save it as todo.html and open it in your browser.

<!DOCTYPE html>
<html>
<body>
  <h1>My tasks</h1>
  <input id="task" placeholder="New task">
  <button id="add">Add</button>
  <ul id="list"></ul>

  <script>
    const input = document.querySelector("#task");
    const list = document.querySelector("#list");

    document.querySelector("#add").addEventListener("click", () => {
      const text = input.value.trim();
      if (!text) return;               // ignore empty input

      const li = document.createElement("li");
      li.textContent = text;           // textContent is safe from HTML injection
      list.append(li);

      input.value = "";
      input.focus();
    });
  </script>
</body>
</html>

How to read that code

  • querySelector finds the first element matching a CSS selector.
  • addEventListener("click", fn) registers fn to run each time the button is clicked.
  • Inside the handler we read the input's value, create an <li>, set its text and append it to the list.
  • Finally we clear the field and put the cursor back so the user can type the next task.

Toggling classes instead of styles

Rather than setting colours from JavaScript, add or remove a CSS class and let the stylesheet decide how it looks. This keeps design in CSS and behaviour in JavaScript.

<style>
  .done { text-decoration: line-through; color: gray; }
</style>
<ul id="list"><li>Buy milk</li><li>Write code</li></ul>
<script>
  document.querySelector("#list").addEventListener("click", (event) => {
    if (event.target.matches("li")) {
      event.target.classList.toggle("done");
    }
  });
</script>

Debugging DOM code

  • null errors usually mean your selector found nothing. Check the spelling and that the script runs after the element exists.
  • Open DevTools, choose the Elements tab and confirm what the page really contains.
  • Log the event: console.log(event.target) shows exactly what was clicked.

Key takeaways

  • The DOM is a live tree of objects the browser builds from your HTML.
  • Find with querySelector, change with textContent and classList, add with createElement and append.
  • React to users with addEventListener; use one listener on a parent for many children.
  • Prefer textContent over innerHTML for user text.
// Write your solution here
Course completeYou finished JavaScriptReview the full course or pick your next one.