The DOM: Understanding the Tree and Selecting Elements

First lesson of the DOM and Events course, free to read: about 15 minutes, 3 exercises and 3 quiz questions. This is the catalogue's base lesson; in the student space, it is rewritten for your profile.

Objectives

  • Explain what the DOM is and how it relates to HTML
  • Load a script at the right time (defer, type="module")
  • Select elements with querySelector and querySelectorAll

The DOM treeSelecting elements

When the browser loads a page, it reads the HTML and builds in memory a tree of objects: the DOM (Document Object Model). Each tag becomes an element object, with properties and methods. JavaScript can read and modify this tree; the browser then updates the display automatically.

<body>
  <h1>Store</h1>
  <ul class="produits">
    <li class="produit" data-id="12">Sac <span class="prix">59 €</span></li>
    <li class="produit" data-id="15">Gourde <span class="prix">12 €</span></li>
  </ul>
</body>

Here, body has two children (h1 and ul), ul has two li children, and so on. The Elements tab of the inspector shows exactly this tree, in its current state (including changes made by JavaScript), whereas “View page source” shows the original HTML.

Loading the script at the right time

A script can only manipulate elements that have already been built. The modern solution:

<head>
  <script src="app.js" defer></script>         <!-- executed after the HTML is parsed -->
  <!-- or -->
  <script type="module" src="app.js"></script> <!-- modules are deferred by default -->
</head>

Without defer or type="module", a script placed in the <head> runs before the <body> exists: your selections will return null.

Selecting with CSS selectors

You already know CSS selectors: they are also used in JavaScript.

// first matching element, or null if there is none
const titre = document.querySelector("h1");
const liste = document.querySelector(".produits");
const sac = document.querySelector('[data-id="12"]');

// all matching elements: a NodeList
const produits = document.querySelectorAll(".produit");
console.log(produits.length);            // 2

for (const p of produits) {              // a NodeList is iterated with for...of
  console.log(p.textContent);
}
produits.forEach(p => console.log(p.dataset.id));  // or forEach

// to use map/filter, convert to a real array
const ids = [...produits].map(p => p.dataset.id);  // ["12", "15"]

The NodeList returned by querySelectorAll is static: it doesn’t update if elements are added afterwards. You have to select again.

document.getElementById("panier") is still common for an id (without the #). The older getElementsByClassName methods return “live” collections with more surprising behavior: prefer querySelectorAll.

Searching inside an element and navigating

const premier = document.querySelector(".produit");
premier.querySelector(".prix");     // searches only inside this li

premier.parentElement;              // the <ul>
premier.children;                   // its child elements
premier.nextElementSibling;         // the next li
premier.closest("ul");              // the nearest matching ancestor (or itself)
premier.matches(".produit");        // true: does the element match the selector?

closest will be very useful for events, in the lesson on delegation.

Planning for absence

const banniere = document.querySelector(".banniere-promo");
if (banniere) {
  banniere.hidden = false;
}
// or with optional chaining for a read:
const texte = document.querySelector(".slogan")?.textContent ?? "";

Calling a method on null causes the classic error Cannot read properties of null. If you see it, check your selector (typo, forgotten dot before a class) and the script’s loading time.

Testing in the console

Open the browser console on any page and type document.querySelectorAll("a").length: you have just queried the DOM. It is the best playground for experimenting.

Exercises

Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.

  1. CodeDifficulty 1JavaScript in the browser2 hints

    Build a CSS selector

    Pure logic (no DOM): write construireSelecteur({ tag, id, classes }) that returns the corresponding CSS selector. All fields are optional; the order is tag, then #id, then the classes prefixed with a dot. Example: { tag: "li", classes: ["produit", "promo"] } gives "li.produit.promo".

    function construireSelecteur({ tag = "", id, classes = [] } = {}) {
      // votre code
    }
  2. CodeDifficulty 2HTML page in the browser2 hints

    Select and count

    In the script: select all the .produit elements, then write the text "3 produits" into #compteur (the number must be computed). Then add the class promo-visible to each product that contains a .promo element.

    <p id="compteur"></p>
    <ul>
      <li class="produit">Sac <span class="promo">-20 %</span></li>
      <li class="produit">Gourde</li>
      <li class="produit">Lampe <span class="promo">-10 %</span></li>
    </ul>
    <script>
      // votre code
    </script>
  3. Multiple choiceDifficulty 11 hint

    Result of an empty selection

    What does document.querySelector(".inexistant") return?

    • An empty NodeList
    • null
    • undefined

End-of-lesson quiz

  1. Why add defer to a

    • To load the script later in the day
    • To run it after the HTML is fully parsed
    • To make it asynchronous with respect to the server
  2. How do you apply map to a NodeList?

    • liste.map(...)
    • [...liste].map(...)
    • liste.toArray().map(...)
  3. What does element.closest(“.carte”) do?

    • Returns the nearest .carte child
    • Returns the element itself or its nearest matching ancestor
    • Returns the next sibling

The rest of the course

  1. Modifying the DOM: Content, Classes, Attributes, and Creating Elements20 min
  2. Listening to Events20 min
  3. Event Propagation and Delegation15 min
  4. Forms, Interface State, and Local Storage20 min

DOM and Events · Intermediate · 3 h 30 min