Qu’est-ce que le DOM ?
Quand un navigateur charge une page HTML, il n’en conserve pas le texte tel quel. Il le lit et en construit une représentation en mémoire : une structure d’objets que JavaScript peut lire et modifier. Cette structure est le DOM.
Une fois la page chargée, on ne manipule plus le HTML mais le DOM. Cette distinction explique qu’une page puisse changer sans être rechargée : modifier le DOM met aussitôt à jour ce que voit l’utilisateur.
Le pont entre deux mondes
JavaScript est un langage (variables, fonctions, objets) et HTML une structure de page. Le DOM est l’interface qui relie les deux : il expose chaque élément HTML comme un objet JavaScript, doté de propriétés (son texte, ses attributs, ses enfants) et de méthodes (le modifier, lui attacher un écouteur d’événement).
L’arbre du document
Le terme « arbre » désigne une structure précise : chaque élément a un parent et peut avoir des enfants, et l’ensemble descend d’une racine unique (<html>), comme une arborescence de fichiers descend d’un dossier racine.
L’exemple ci-dessous montre une carte affichant une boîte à livres. À gauche, ce que voit l’utilisateur ; à droite, l’arbre DOM correspondant. Survoler un élément d’un côté illumine son jumeau de l’autre ; cliquer sur un nœud affiche ses détails.
Cet inspecteur reproduit ce que fait l’onglet Elements (ou Inspecteur) des outils de développement du navigateur. Chaque chose visible à l’écran correspond à un nœud de l’arbre, et inversement. Savoir circuler entre ces deux vues est une compétence de base du développement web.
Vocabulaire de l’arbre
- Nœud (node) : tout élément de l’arbre, une balise, mais aussi le texte qu’elle contient ou un commentaire.
- Élément (element) : un nœud qui est une balise HTML (
<p>,<div>,<a>). - Parent : le nœud qui en contient directement un autre.
- Enfant (child) : un nœud contenu directement dans un autre.
- Frères (siblings) : des nœuds qui partagent le même parent.
- Racine (root) : le sommet de l’arbre, l’élément
<html>.
VérificationDans la carte ci-dessus, quel est le parent des trois <li> ? Et leurs frères ?
Le parent des trois <li> est le <ul> (la liste des tags). Les trois <li> sont frères entre eux, car ils partagent le même parent. Le <ul> est l’enfant de <article> et le frère de <h2>, <p> et <a>.
Sélectionner des éléments
Pour modifier un élément, il faut d’abord l’atteindre depuis JavaScript. Le DOM fournit des méthodes de sélection. Les deux méthodes modernes, employées presque toujours, prennent un sélecteur CSS :
// Le premier élément qui correspond au sélecteur CSS
const titre = document.querySelector(".carte__titre");
// Tous les éléments qui correspondent (une NodeList)
const tags = document.querySelectorAll(".carte__tags li");
Des méthodes plus anciennes restent répandues dans le code existant :
document.getElementById("menu"); // par id (sans le #)
document.getElementsByClassName("carte"); // par classe (collection live)
document.getElementsByTagName("li"); // par balise
Dans du code récent, on préfère querySelector et querySelectorAll : une seule syntaxe à retenir, identique à celle de CSS.
querySelector vs querySelectorAll
| Retourne | Si rien ne correspond | |
|---|---|---|
querySelector(sel) |
le premier élément (ou null) |
null |
querySelectorAll(sel) |
tous les éléments (une NodeList) |
NodeList vide |
Une NodeList ressemble à un tableau et s’itère avec for...of ou forEach, sans en être tout à fait un. Pour utiliser map ou filter, on la convertit d’abord avec Array.from(tags) ou [...tags].
VérificationPourquoi const x = document.querySelector('.inexistant') ne plante-t-il pas, mais x.textContent plante-t-il ?
querySelector retourne null quand aucun élément ne correspond, ce qui ne lève pas d’erreur. Mais null.textContent plante, car null n’a pas de propriété textContent. C’est l’erreur Cannot read properties of null la plus fréquente en DOM. Il faut toujours vérifier que la sélection a réussi avant d’utiliser le résultat, surtout si l’élément pourrait ne pas exister.
Lire et modifier un élément
Une fois un élément sélectionné, on accède à ses propriétés comme à celles de n’importe quel objet JavaScript.
Le contenu
const titre = document.querySelector(".carte__titre");
titre.textContent = "Nouvelle boîte"; // remplace le texte
titre.textContent; // lit le texte actuel
Les attributs
const lien = document.querySelector(".carte__lien");
lien.getAttribute("href"); // lire un attribut
lien.setAttribute("href", "/boites/7"); // modifier un attribut
lien.href = "/boites/7"; // raccourci pour les attributs courants
Les classes CSS
La propriété classList est la façon idiomatique de manipuler les classes, donc l’apparence :
const carte = document.querySelector(".carte");
carte.classList.add("active"); // ajouter une classe
carte.classList.remove("active"); // retirer
carte.classList.toggle("active"); // basculer (ajoute si absente, retire si présente)
carte.classList.contains("active"); // tester (true / false)
Modifier les classes plutôt que les styles directement garde la logique d’apparence dans le CSS. JavaScript décide quand une classe s’applique, CSS décide à quoi elle ressemble.
Créer et insérer des éléments
On peut fabriquer de nouveaux nœuds et les greffer dans l’arbre :
// 1. Créer l'élément
const nouveauTag = document.createElement("li");
nouveauTag.textContent = "poésie";
// 2. L'insérer dans le DOM
const liste = document.querySelector(".carte__tags");
liste.appendChild(nouveauTag); // ajoute à la fin des enfants
Tant qu’un élément créé n’est pas inséré dans l’arbre, il existe en mémoire mais n’apparaît nulle part à l’écran : il reste détaché. C’est l’insertion (appendChild, prepend, before, after) qui le rend visible.
Pour retirer un élément :
nouveauTag.remove(); // se retire lui-même de l'arbre
VérificationOn a fait document.createElement('p') et défini son textContent, mais rien n'apparaît. Pourquoi ?
Parce que l’élément a été créé en mémoire mais jamais inséré dans l’arbre du document. Un élément créé est détaché : il faut l’attacher à un parent déjà présent dans le DOM avec appendChild, prepend, before ou after pour qu’il devienne visible. Créer n’est pas insérer.
Réagir aux événements
Le DOM ne sert pas qu’à modifier la page : il permet aussi de réagir à ce que fait l’utilisateur, qu’il s’agisse d’un clic, d’une frappe au clavier, d’un survol ou de la soumission d’un formulaire. On attache pour cela un écouteur d’événement (event listener) à un élément.
const bouton = document.querySelector(".carte__lien");
bouton.addEventListener("click", (evenement) => {
evenement.preventDefault(); // empêche le comportement par défaut
console.log("La boîte a été cliquée");
});
addEventListener prend deux arguments : le type d’événement ("click", "input", "submit", "mouseover"…) et une fonction exécutée quand il survient. Cette fonction reçoit un objet evenement décrivant ce qui s’est produit.
Les pièces précédentes se combinent ici. Une page interactive forme une boucle : l’utilisateur agit, un événement se déclenche, JavaScript modifie le DOM, et l’affichage change.
// Exemple complet : un compteur de clics
const bouton = document.querySelector("#compteur");
let clics = 0;
bouton.addEventListener("click", () => {
clics++;
bouton.textContent = `Cliqué ${clics} fois`;
});
VérificationÀ quoi sert evenement.preventDefault() dans le clic sur un lien <a> ?
Un lien <a href="..."> a un comportement par défaut : naviguer vers l’URL. preventDefault() annule ce comportement, ce qui permet d’exécuter du JavaScript à la place (par exemple afficher du contenu sans changer de page). Sans cet appel, le navigateur suivrait le lien et quitterait la page avant l’exécution du reste du code.