Manipuler le DOM en JavaScript : le guide pratique
Manipuler le DOM, c’est ce qui transforme une page HTML statique en interface vivante. Le DOM (Document Object Model) est la représentation en objets de votre page, que JavaScript peut lire et modifier à la volée : changer un texte, ajouter un élément, réagir à un clic. Ce guide couvre chaque opération, de la sélection d’un élément à la délégation d’événements, avec un exemple concret pour chaque cas. C’est le cœur du développement web côté navigateur.
Sélectionner des éléments
Tout commence par récupérer une référence vers l’élément à manipuler. Les deux méthodes modernes sont querySelector et querySelectorAll, qui acceptent n’importe quel sélecteur CSS.
// Le premier élément qui correspond au sélecteur CSS
const titre = document.querySelector("h1");
const bouton = document.querySelector(".btn-primary");
const champ = document.querySelector("#email");
const premierItem = document.querySelector("ul li:first-child");
// Tous les éléments correspondants → NodeList
const items = document.querySelectorAll(".item");
// Méthodes historiques, toujours valides
const parId = document.getElementById("email");
const parClasse = document.getElementsByClassName("item"); // HTMLCollection live
const parBalise = document.getElementsByTagName("p");
querySelectorAll renvoie une NodeList que l’on parcourt facilement avec forEach.
document.querySelectorAll(".item").forEach((item) => {
console.log(item.textContent);
});
On peut aussi restreindre une recherche à l’intérieur d’un élément, plutôt que sur tout le document.
const carte = document.querySelector(".carte");
const titreCarte = carte.querySelector("h2"); // cherche seulement dans .carte
Modifier le texte
Pour lire ou écrire du texte, textContent est le choix par défaut : il traite le contenu comme du texte brut, sans interpréter le HTML.
const titre = document.querySelector("h1");
titre.textContent = "Nouveau titre"; // écrit
const actuel = titre.textContent; // lit
// textContent échappe le HTML — sûr contre l'injection
titre.textContent = "<b>pas gras</b>"; // affiche littéralement les balises
Modifier le HTML
innerHTML interprète les balises. Puissant, mais à réserver au contenu que vous contrôlez : n’y injectez jamais de saisie utilisateur brute, sous peine de faille XSS.
const conteneur = document.querySelector(".conteneur");
conteneur.innerHTML = "<p>Un <strong>paragraphe</strong> riche.</p>";
// Ajouter sans tout écraser
conteneur.innerHTML += "<p>Second paragraphe.</p>";
// insertAdjacentHTML : plus précis, sans réanalyser tout le contenu
conteneur.insertAdjacentHTML("beforeend", "<p>Ajouté à la fin.</p>");
// Positions : beforebegin, afterbegin, beforeend, afterend
Modifier les attributs
Les attributs HTML (href, src, disabled, data-…) se lisent et s’écrivent avec des méthodes dédiées.
const lien = document.querySelector("a");
lien.getAttribute("href"); // lire
lien.setAttribute("href", "/contact"); // écrire
lien.hasAttribute("target"); // tester
lien.removeAttribute("target"); // supprimer
// Propriétés directes pour les attributs courants
lien.href = "/accueil";
const img = document.querySelector("img");
img.src = "/logo.png";
img.alt = "Logo";
// Attributs data-*
const el = document.querySelector(".produit");
el.dataset.prix = "49"; // écrit data-prix="49"
console.log(el.dataset.prix); // lit "49"
Modifier les classes CSS
L’objet classList gère les classes proprement, sans écraser les autres.
const boite = document.querySelector(".boite");
boite.classList.add("active"); // ajoute
boite.classList.remove("cachee"); // retire
boite.classList.toggle("ouvert"); // bascule (ajoute/retire)
boite.classList.contains("active"); // teste → true/false
boite.classList.replace("v1", "v2"); // remplace
// toggle avec condition
boite.classList.toggle("valide", champ.value !== "");
Modifier les styles
On agit directement sur la propriété style (styles inline) ou, mieux, on bascule des classes définies en CSS.
const boite = document.querySelector(".boite");
boite.style.color = "white";
boite.style.backgroundColor = "navy"; // camelCase pour background-color
boite.style.display = "none";
// Plusieurs propriétés d'un coup avec cssText
boite.style.cssText = "color: white; padding: 1rem;";
// Lire le style calculé (résultat final, y compris CSS externe)
const couleur = getComputedStyle(boite).color;
// Variables CSS
boite.style.setProperty("--accent", "#e11");
En pratique, préférez classList à style : garder l’apparence dans le CSS rend le code plus maintenable.
Créer et insérer des éléments
Pour construire du contenu proprement, on crée les éléments en JavaScript puis on les insère.
// 1. Créer
const item = document.createElement("li");
item.textContent = "Nouvel élément";
item.classList.add("item");
// 2. Insérer dans un parent
const liste = document.querySelector("ul");
liste.append(item); // à la fin (accepte texte + plusieurs nœuds)
liste.prepend(item); // au début
liste.appendChild(item); // équivalent historique, à la fin
// Insérer avant/après un élément précis
const reference = document.querySelector(".item");
reference.before(item); // juste avant
reference.after(item); // juste après
Quand on ajoute beaucoup d’éléments, un DocumentFragment évite de toucher la page à chaque itération, ce qui améliore les performances.
const fragment = document.createDocumentFragment();
["Paris", "Lyon", "Nice"].forEach((ville) => {
const li = document.createElement("li");
li.textContent = ville;
fragment.append(li);
});
document.querySelector("ul").append(fragment); // une seule insertion
Cette boucle s’appuie sur les méthodes de tableau pour générer le contenu.
Supprimer et remplacer des éléments
La suppression est directe avec remove().
const el = document.querySelector(".obsolete");
el.remove(); // se retire lui-même du DOM
// Vider un conteneur
const liste = document.querySelector("ul");
liste.replaceChildren(); // supprime tous les enfants
// Remplacer un élément par un autre
const nouveau = document.createElement("p");
nouveau.textContent = "Remplaçant";
document.querySelector(".ancien").replaceWith(nouveau);
// Cloner un élément
const copie = el.cloneNode(true); // true = avec les enfants
Naviguer dans l’arbre du DOM
Depuis un élément, on peut atteindre ses voisins, parents et enfants.
const el = document.querySelector(".item");
el.parentElement; // parent direct
el.children; // enfants (éléments seulement)
el.firstElementChild; // premier enfant
el.lastElementChild; // dernier enfant
el.nextElementSibling; // frère suivant
el.previousElementSibling; // frère précédent
el.closest(".conteneur"); // ancêtre le plus proche correspondant
Gérer les événements
addEventListener attache une fonction qui s’exécute quand un événement se produit (clic, saisie, envoi de formulaire…).
const bouton = document.querySelector(".btn");
bouton.addEventListener("click", (event) => {
console.log("Bouton cliqué !");
console.log(event.target); // l'élément déclencheur
});
// Événements courants
const champ = document.querySelector("input");
champ.addEventListener("input", (e) => console.log(e.target.value));
champ.addEventListener("focus", () => console.log("champ actif"));
const formulaire = document.querySelector("form");
formulaire.addEventListener("submit", (e) => {
e.preventDefault(); // empêche le rechargement de la page
console.log("Formulaire soumis");
});
On retire un écouteur avec removeEventListener, à condition d’avoir une référence à la fonction (pas une fonction anonyme).
function gerer() {
console.log("clic");
}
bouton.addEventListener("click", gerer);
bouton.removeEventListener("click", gerer);
// Écouteur qui ne se déclenche qu'une fois
bouton.addEventListener("click", gerer, { once: true });
Ces callbacks reposent sur les fonctions fléchées, attention toutefois au this : dans une fonction fléchée, this n’est pas l’élément ; utilisez event.currentTarget à la place.
La délégation d’événements
Attacher un écouteur à chaque élément d’une longue liste est coûteux, et ne fonctionne pas pour les éléments ajoutés après coup. La délégation résout les deux problèmes : on écoute sur le parent et on identifie la cible grâce à la propagation des événements (event bubbling).
const liste = document.querySelector("ul");
// Un seul écouteur pour tous les <li>, présents et futurs
liste.addEventListener("click", (event) => {
const item = event.target.closest("li");
if (!item) return; // clic hors d'un li
console.log("Clic sur :", item.textContent);
item.classList.toggle("selectionne");
});
Ce patron est essentiel dans les interfaces dynamiques : un seul écouteur gère un nombre illimité d’éléments, y compris ceux insérés plus tard.
À retenir
Manipuler le DOM suit toujours le même cycle : sélectionner un élément (querySelector), le modifier (texte, HTML, attributs, classes, styles), ou créer/insérer/supprimer des nœuds, puis réagir aux actions de l’utilisateur avec addEventListener. Deux réflexes de qualité : préférez textContent et classList à innerHTML et style quand c’est possible, et utilisez la délégation d’événements dès que vous manipulez des listes dynamiques. Avec ces bases, vous pouvez construire n’importe quelle interaction côté navigateur.