Quand le navigateur charge une page, il lit le HTML et construit en mémoire un arbre d’objets : le DOM (Document Object Model). Chaque balise devient un objet élément, avec des propriétés et des méthodes. JavaScript peut lire et modifier cet arbre ; le navigateur met alors l’affichage à jour automatiquement.
<body>
<h1>Boutique</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>
Ici, body a deux enfants (h1 et ul), ul a deux enfants li, etc. L’onglet Éléments de l’inspecteur montre exactement cet arbre, dans son état actuel (y compris les modifications faites par JavaScript), alors que « Afficher le code source » montre le HTML d’origine.
Charger le script au bon moment
Un script ne peut manipuler que des éléments déjà construits. La solution moderne :
<head>
<script src="app.js" defer></script> <!-- exécuté après l'analyse du HTML -->
<!-- ou -->
<script type="module" src="app.js"></script> <!-- les modules sont différés par défaut -->
</head>
Sans defer ni type="module", un script placé dans le <head> s’exécute avant que le <body> existe : vos sélections renverront null.
Sélectionner avec les sélecteurs CSS
Vous connaissez déjà les sélecteurs CSS : ils servent aussi en JavaScript.
// premier élément correspondant, ou null s'il n'y en a pas
const titre = document.querySelector("h1");
const liste = document.querySelector(".produits");
const sac = document.querySelector('[data-id="12"]');
// tous les éléments correspondants : une NodeList
const produits = document.querySelectorAll(".produit");
console.log(produits.length); // 2
for (const p of produits) { // une NodeList se parcourt avec for...of
console.log(p.textContent);
}
produits.forEach(p => console.log(p.dataset.id)); // ou forEach
// pour utiliser map/filter, on convertit en vrai tableau
const ids = [...produits].map(p => p.dataset.id); // ["12", "15"]
La NodeList renvoyée par querySelectorAll est statique : elle ne se met pas à jour si on ajoute des éléments ensuite. Il faut refaire la sélection.
document.getElementById("panier") reste courant pour un id (sans le #). Les anciennes méthodes getElementsByClassName renvoient des collections « vivantes » au comportement plus surprenant : préférez querySelectorAll.
Chercher dans un élément et naviguer
const premier = document.querySelector(".produit");
premier.querySelector(".prix"); // cherche seulement à l'intérieur de ce li
premier.parentElement; // le <ul>
premier.children; // ses éléments enfants
premier.nextElementSibling; // le li suivant
premier.closest("ul"); // l'ancêtre le plus proche qui correspond (ou lui-même)
premier.matches(".produit"); // true : l'élément correspond-il au sélecteur ?
closest sera très utile pour les événements, dans la leçon sur la délégation.
Prévoir l’absence
const banniere = document.querySelector(".banniere-promo");
if (banniere) {
banniere.hidden = false;
}
// ou avec le chaînage optionnel pour une lecture :
const texte = document.querySelector(".slogan")?.textContent ?? "";
Appeler une méthode sur null provoque l’erreur classique Cannot read properties of null. Si vous la voyez, vérifiez votre sélecteur (faute de frappe, point oublié devant une classe) et le moment de chargement du script.
Tester dans la console
Ouvrez la console du navigateur sur n’importe quelle page et tapez document.querySelectorAll("a").length : vous venez d’interroger le DOM. C’est le meilleur terrain de jeu pour expérimenter.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Construire un sélecteur CSS
Logique pure (sans DOM) : écrivez
construireSelecteur({ tag, id, classes })qui renvoie le sélecteur CSS correspondant. Tous les champs sont facultatifs ; l’ordre est balise, puis#id, puis les classes préfixées par un point. Exemple :{ tag: "li", classes: ["produit", "promo"] }donne"li.produit.promo".function construireSelecteur({ tag = "", id, classes = [] } = {}) { // votre code }Sélectionner et compter
Dans le script : sélectionnez tous les
.produit, puis écrivez dans#compteurle texte"3 produits"(le nombre doit être calculé). Ajoutez ensuite la classepromo-visibleà chaque produit qui contient un élément.promo.<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>Résultat d'une sélection vide
Que renvoie
document.querySelector(".inexistant")?- Une NodeList vide
- null
- undefined
Quiz de fin de leçon
Pourquoi ajouter defer à une balise
- Pour charger le script plus tard dans la journée
- Pour l'exécuter après l'analyse complète du HTML
- Pour le rendre asynchrone vis-à-vis du serveur
Comment appliquer map à une NodeList ?
- liste.map(...)
- [...liste].map(...)
- liste.toArray().map(...)
Que fait element.closest(“.carte”) ?
- Renvoie l'enfant .carte le plus proche
- Renvoie l'élément lui-même ou son ancêtre le plus proche qui correspond
- Renvoie le voisin suivant
La suite du cours
- Modifier le DOM : contenu, classes, attributs et création d'éléments20 min
- Écouter les événements20 min
- Propagation et délégation d'événements15 min
- Formulaires, état de l'interface et stockage local20 min