Question 1Avec 3 li qui ont data-id, que renvoie querySelector("[data-id]") ?
L'arbre DOM et la sélection d'éléments — querySelector et querySelectorAll
querySelector et querySelectorAll pour récupérer des éléments dans l'arbre DOM : recherche par classe, id et attribut data, conversion d'une NodeList en tableau, et ?. quand l'élément manque.
Tu veux lire le prix de tous les produits d'une liste. Chercher par id, comme #total, ne marche que pour les éléments qui ont un id : ce n'est pas adapté à une liste de produits qui ont tous la même structure.
Cet article couvre les sélecteurs, qui trouvent des éléments selon d'autres critères que l'id, querySelectorAll, qui rassemble toutes les correspondances, et la façon de lire un élément qui risque d'être absent.
Récupérer un seul élément — querySelector et les sélecteurs
Dans une liste d'annonces, tu ne veux lire que le titre de l'annonce importante. La liste est faite de li identiques, dont aucun n'a d'id. Un id est un nom qu'un seul élément de la page peut porter : il ne peut donc pas servir à marquer une catégorie, comme « les annonces importantes ».
Un sélecteur (une chaîne qui décrit l'élément recherché) désigne des éléments par nom de balise, comme li, par id, comme #news, ou par classe (un nom que tu donnes à tous les éléments du même type), comme .notice. document.querySelector(sélecteur) renvoie le premier élément de l'arbre DOM qui correspond.
// Préparer une liste d'annonces (un élément peut avoir plusieurs classes, séparées par des espaces)
document.body.innerHTML = `<ul id="news">
<li class="notice">Frais de port modifiés</li>
<li class="notice important">Maintenance imprévue</li>
<li class="notice">Nouveautés d'automne</li>
</ul>`;
// Chercher par nom de balise / par classe (le nom de la classe suit le .)
console.log(document.querySelector("li").textContent); // Frais de port modifiés
console.log(document.querySelector(".notice").textContent); // Frais de port modifiés
console.log(document.querySelector(".important").textContent); // Maintenance imprévue
"li"et".notice"— s'arrêtent tous les deux ici- Texte : « Frais de port modifiés »
".important"— s'arrête ici- Texte : « Maintenance imprévue »
- Texte : « Nouveautés d'automne »
- Aucun des trois sélecteurs ne va jusqu'ici
".notice" arrête de chercher au premier. Tu récupères le premier élément qui correspond.Le deuxième li a deux classes : il correspond à la fois à .notice et à .important. Pour ne récupérer que lui, écris un critère auquel seul ce deuxième li correspond, comme .important.
Parcourir toute une liste — querySelectorAll et NodeList
Et si tu veux les titres de toutes les annonces d'un coup ? querySelector arrête de chercher à la première correspondance : tu as beau l'appeler plusieurs fois avec le même sélecteur, tu obtiens toujours « Frais de port modifiés », jamais la deuxième annonce ni les suivantes.
document.querySelectorAll(sélecteur) renvoie tous les éléments qui correspondent dans une NodeList (un objet semblable à un tableau qui contient les éléments trouvés, dans l'ordre de l'arbre DOM). length donne leur nombre, et forEach permet de les traiter un par un.
document.body.innerHTML = `<ul id="news">
<li class="notice">Frais de port modifiés</li>
<li class="notice important">Maintenance imprévue</li>
<li class="notice">Nouveautés d'automne</li>
</ul>`;
// Récupérer tous les éléments qui correspondent dans une NodeList
const notices = document.querySelectorAll(".notice");
console.log(notices.length); // 3
notices.forEach((notice) => console.log(notice.textContent));
// Frais de port modifiés
// Maintenance imprévue
// Nouveautés d'automne
// notices.map(...) lève une TypeError : convertir d'abord en tableau
const titles = [...notices].map((notice) => notice.textContent);
console.log(titles.join(" / ")); // Frais de port modifiés / Maintenance imprévue / Nouveautés d'automne
Même sous forme de NodeList, tu peux lire un élément par son index, comme notices[1]. Le tableau créé avec la syntaxe de décomposition est un objet distinct : après la conversion, notices reste une NodeList, et tu peux continuer à l'utiliser avec forEach.
Désigner un élément par son numéro — attributs data et sélecteurs d'attribut
Cette fois, il te faut uniquement l'annonce numéro N-103. Si les mêmes annonces apparaissent aussi ailleurs sur la page, les id seront en double : l'id ne convient donc pas. Quant à la classe, elle sert à regrouper des éléments d'un même type, et ce n'est pas le bon endroit pour ranger un numéro.
Un attribut data (un attribut dont le nom commence par data-) peut contenir n'importe quelle valeur personnalisée, comme un numéro. Les sélecteurs qui cherchent par attribut s'écrivent entre crochets : [data-id] correspond aux éléments qui ont l'attribut, et [data-id="N-103"] à ceux dont la valeur correspond aussi.
document.body.innerHTML = `<ul id="news">
<li class="notice" data-id="N-101">Frais de port modifiés</li>
<li class="notice important" data-id="N-102">Maintenance imprévue</li>
<li class="notice" data-id="N-103">Nouveautés d'automne</li>
</ul>`;
// Chercher uniquement selon la présence de l'attribut (les 3 correspondent)
console.log(document.querySelector("[data-id]").textContent); // Frais de port modifiés
// Chercher un élément dont la valeur correspond aussi (la valeur est entre ", donc le sélecteur entier va entre ')
console.log(document.querySelector('[data-id="N-103"]').textContent); // Nouveautés d'automne
Sans valeur, [data-id] correspond aux trois et renvoie le premier ; avec la valeur, [data-id="N-103"] ne désigne que celui dont le numéro correspond. Le tableau ci-dessous reprend les sélecteurs utilisés dans cet article et l'élément que chacun renvoie dans la liste d'annonces.
| Sélecteur | Correspond à | Renvoyé dans les annonces |
|---|---|---|
| li | Balise li | 1er li (Frais de port…) |
| .important | class contient important | 2e li (Maintenance…) |
| #news | id vaut news | L'élément ul |
| [data-id] | A un attribut data-id | 1er li (Frais de port…) |
| [data-id="N-103"] | data-id vaut N-103 | 3e li (Nouveautés…) |
Sans guillemets, une valeur qui commence par un chiffre échoue
Certaines valeurs marchent sans guillemets, comme [data-id=N-103], mais une valeur qui commence par un chiffre, comme [data-id=102], ne peut pas être lue comme un sélecteur : la ligne lève alors une erreur « is not a valid selector ». Entoure toujours les valeurs de ".
Continuer même si l'élément manque — null et ?.
Prends une page d'accueil qui n'affiche un titre que s'il y a une annonce urgente. Les jours où il n'y en a pas, l'élément n'existe pas : la ligne qui lit le texte du titre lève une TypeError, et rien de ce qui suit ne s'exécute.
Quand aucun élément ne correspond, querySelector renvoie null. Si tu ajoutes le chaînage optionnel ?. au résultat avant de lire .textContent, il renvoie undefined sans aller plus loin quand la valeur est null, et un ?? placé ensuite permet de fixer une valeur par défaut.
// Une liste sans annonce urgente (classe urgent) pour l'instant
document.body.innerHTML = `<ul><li class="notice">Nouveautés d'automne</li></ul>`;
const urgent = document.querySelector(".urgent");
console.log(urgent); // null
// urgent.textContent lèverait une TypeError
console.log(urgent?.textContent); // undefined
console.log(urgent?.textContent ?? "Aucune"); // Aucune
// Dans une liste qui a une annonce urgente, comparer avec et sans le .
document.body.innerHTML = `<ul><li class="urgent">Panne du serveur</li></ul>`;
console.log(document.querySelector(".urgent")?.textContent ?? "Aucune"); // Panne du serveur
console.log(document.querySelector("urgent")?.textContent ?? "Aucune"); // Aucune
Réserve ?. aux éléments qui peuvent manquer certains jours, comme l'annonce urgente. Si tu l'ajoutes aussi aux éléments toujours présents, tu ne verras plus tes fautes de frappe : n'en mets pas, laisse le code lever une TypeError, et sers-toi du numéro de ligne pour trouver la cause.
Une NodeList vide n'est pas null
Même quand rien ne correspond, querySelectorAll ne renvoie pas null, mais une NodeList dont length vaut 0. Appeler forEach dessus ne lève pas d'erreur et ne fait tout simplement rien : pour savoir qu'aucun élément n'a été trouvé, vérifie length === 0 plutôt que d'utiliser ?..
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Que se passe-t-il si tu appelles map directement sur la valeur renvoyée par querySelectorAll(".notice") ?
Question 3Quand l'élément n'existe pas, quelle est la valeur de querySelector(".sale-price")?.textContent ?