Les outils de navigation du DOM sont très pratiques quand les éléments sont proches les uns des autres. Mais sâils ne le sont pas ? Comment atteindre un élément arbitraire de la page ?
Il existe dâautres méthodes de recherche pour cela.
document.getElementById ou juste id
Si un élément a lâattribut id, on peut atteindre cet élément en utilisant la méthode document.getElementById(id), peu importe où elle se trouve.
Par exemple :
<div id="elem">
<div id="elem-content">Elément</div>
</div>
<script>
// récupération de l'élément :
let elem = document.getElementById('elem');
// on met son arrière-plan rouge :
elem.style.background = 'red';
</script>
Il y a aussi une variable globale nommée selon lâid qui référence lâélément :
<div id="elem">
<div id="elem-content">Elément</div>
</div>
<script>
// elem est une référence à l'élément du DOM ayant l'id "elem"
elem.style.background = 'red';
// id="elem-content" contient un tiret, donc ça ne peut pas être un nom de variable
// ...mais on peut y accéder en utilisant les crochets : window['elem-content']
</script>
â¦A moins quâon déclare une variable JavaScript avec le même nom, auquel cas celle-ci obtient la priorité :
<div id="elem"></div>
<script>
let elem = 5; // maintenant elem vaut 5, ce n'est plus une référence à <div id="elem">
alert(elem); // 5
</script>
Ce comportement est décrit dans la spécification, mais il est pris en charge principalement pour la compatibilité .
Le navigateur essaie de nous aider en mélangeant les noms de JS et du DOM. Câest bien pour des scripts simples, intégré dans du HTML, mais en général ce nâest pas bon. Il peut y avoir des conflits de noms. Aussi, quand quelquâun lira le code JS sans avoir le HTML à côté, ce ne sera pas évident pour lui dâoù vient la variable.
Dans ce tutoriel, on utilise id pour directement référencer un élément rapidement, quand il sera évident dâoù il vient.
Dans la vraie vie, document.getElementById est la méthode à avantager.
id doit être uniqueLâid doit être unique. Il ne peut y avoir quâun élément dans le document avec un id donné.
Sâil y a de multiples éléments avec le même id, alors le comportement de la méthode qui lâutilise est imprévisible, par exemple document.getElementById pourra renvoyer nâimporte lequel de ces éléments aléatoirement. Donc suivez la règle et gardez lâid unique.
document.getElementById, pas anyElem.getElementByIdLa méthode getElementById ne peut être appelée que sur lâobjet document . Elle va chercher lâid dans le document entier.
querySelectorAll
De loin, la méthode la plus polyvalente, elem.querySelectorAll(css) renvoie tous les éléments à lâintérieur de elem correspondant au sélecteur CSS donné en paramètre
Ici, on recherche tous les éléments <li> qui sont les derniers enfants :
<ul>
<li>Le</li>
<li>test</li>
</ul>
<ul>
<li>a</li>
<li>réussi</li>
</ul>
<script>
let elements = document.querySelectorAll('ul > li:last-child');
for (let elem of elements) {
alert(elem.innerHTML); // "test", "réussi"
}
</script>
Cette méthode est très puissante, car tous les sélecteurs CSS peuvent être utilisés.
Les pseudo-classes dans le sélecteur CSS comme :hover et :active sont aussi acceptés. Par exemple, document.querySelectorAll(':hover') renverra lâensemble des éléments dont le curseur est au-dessus en ce moment (dans lâordre dâimbrication : du plus extérieur <html> au plus imbriqué).
querySelector
Un appel à elem.querySelector(css) renverra le premier élément dâun sélecteur CSS donné.
En dâautres termes, le résultat sera le même que elem.querySelectorAll(css)[0], mais celui-ci cherchera tous les éléments et en choisira un seul, alors que elem.querySelector nâen cherchera quâun. Câest donc plus rapide, et plus court à écrire.
matches
Les méthodes précédentes recherchaient dans le DOM.
La commande elem.matches(css) ne recherche rien, elle vérifie simplement que elem correspond au sélecteur CSS donné. Elle renvoie true ou false.
Cette méthode devient utile quand on itère sur des éléments (comme dans un array par exemple) et quâon veut filtrer ceux qui nous intéressent.
Par exemple :
<a href="http://example.com/file.zip">...</a>
<a href="http://ya.ru">...</a>
<script>
// on peut mettre n'importe quel ensemble à la place de document.body.children
for (let elem of document.body.children) {
if (elem.matches('a[href$="zip"]')) {
alert("le lien de l'archive : " + elem.href );
}
}
</script>
closest
Les ancêtres dâun élément sont : le parent, le parent du parent, son propre parent etc⦠Les ancêtres forment une chaîne de parents depuis lâélément jusquâau sommet.
La méthode elem.closest(css) cherche lâancêtre le plus proche qui correspond au sélecteur CSS. Lâélément elem est lui-même inclus dans la recherche.
En dâautres mots, la méthode closest part de lâélément et remonte en regardant chacun des parents. Sâil correspond au sélecteur, la recherche sâarrête et lâancêtre est renvoyé.
Par exemple :
<h1>Contenu</h1>
<div class="contents">
<ul class="book">
<li class="chapter">Chapître 1</li>
<li class="chapter">Chapître 2</li>
</ul>
</div>
<script>
let chapter = document.querySelector('.chapter'); // LI
alert(chapter.closest('.book')); // UL
alert(chapter.closest('.contents')); // DIV
alert(chapter.closest('h1')); // null (car h1 n'est pas un ancêtre)
</script>
getElementsBy*
Il y a aussi dâautres méthodes pour rechercher des balises par tag, classe, etcâ¦
Aujourdâhui, elles sont principalement de lâhistoire ancienne, car querySelector est plus puissante et plus courte à écrire.
Donc ici, on va surtout en parler dans le souci dâêtre complet, comme elles peuvent encore se retrouver dans des vieux scripts.
elem.getElementsByTagName(tag)cherche les éléments avec le tag donné et renvoie lâensemble de ces éléments. Le paramètretagpeut aussi être une étoile"*"pour signifier nâimporte quel tag.elem.getElementsByClassName(className)renvoie les éléments qui ont la classe CSS donnée.document.getElementsByName(name)renvoie les éléments qui ont lâattributname, dans tout le document. Très rarement utilisé.
Par exemple:
// récupérer tous les divs du document.
let divs = document.getElementsByTagName('div');
Trouvons tous les tags input dans le tableau :
<table id="table">
<tr>
<td>Votre âge:</td>
<td>
<label>
<input type="radio" name="age" value="young" checked> moins de 18
</label>
<label>
<input type="radio" name="age" value="mature"> entre 18 et 50
</label>
<label>
<input type="radio" name="age" value="senior"> plus de 60
</label>
</td>
</tr>
</table>
<script>
let inputs = table.getElementsByTagName('input');
for (let input of inputs) {
alert( input.value + ': ' + input.checked );
}
</script>
"s" !Les développeurs junior oublient parfois la lettre "s". Ils essaient donc dâappeler getElementByTagName au lieu de getElementsByTagName.
La lettre "s" letter nâapparaît pas dans getElementById, car cette méthode renvoie un seul élément. Mais getElementsByTagName renvoie un ensemble dâéléments, il y a donc un "s".
Une autre erreur répandue parmi les débutants est dâécrire :
// ne fonctionne pas :
document.getElementsByTagName('input').value = 5;
Cela ne va pas marcher, parce quâon essaie dâaffecter une valeur à un ensemble dâobjets plutôt quâà un élément de cet ensemble. On devrait plutôt itérer sur lâensemble ou récupérer un élément par son index, et lui affecter la valeur, comme ceci :
// doit fonctionner (s'il y a un élément 'input' )
document.getElementsByTagName('input')[0].value = 5;
Recherche des éléments .article :
<form name="my-form">
<div class="article">Article</div>
<div class="long article">Long article</div>
</form>
<script>
// recherche par attribut nom
let form = document.getElementsByName('my-form')[0];
// recherche par classe dans le formulaire
let articles = form.getElementsByClassName('article');
alert(articles.length); // 2 éléments trouvés avec la classe 'article'
</script>
Ensembles courants
Toutes les méthodes "getElementsBy*" renvoient lâensemble courant. De tels ensembles montrent toujours lâétat courant du document et se mettent à jour automatiquement quand celui-ci change.
Dans lâexemple ci-dessous, il y a deux scripts :
- Le premier crée une référence à lâensemble des
<div>. Maintenant, sa longueur est1. - Le second se lance après que le navigateur aie rencontré un autre
<div>, donc sa longueur est2.
<div>Premier div</div>
<script>
let divs = document.getElementsByTagName('div');
alert(divs.length); // 1
</script>
<div>Second div</div>
<script>
alert(divs.length); // 2
</script>
A lâopposé, querySelectorAll renvoie un ensemble statique. Câest comme un tableau fixe dâéléments
Si on lâutilise, alors les deux scripts ci-dessus renvoient 1:
<div>Premier div</div>
<script>
let divs = document.querySelectorAll('div');
alert(divs.length); // 1
</script>
<div>Second div</div>
<script>
alert(divs.length); // 1
</script>
Maintenant, on voit facilement la différence. Lâensemble statique ne sâest pas incrémenté après lâapparition dâun nouveau div dans le document.
Résumé
Il y a 6 principales méthodes pour rechercher des balises dans le DOM :
| Méthode | Recherches par... | Peut appeler un élément ? | Courant ? |
querySelector |
sélecteur CSS | â | - |
querySelectorAll |
sélecteur CSS | â | - |
getElementById |
id |
- | - |
getElementsByName |
nom |
- | â |
getElementsByTagName |
tag ou '*' |
â | â |
getElementsByClassName |
classe | â | â |
De loin, les plus utilisées sont querySelector et querySelectorAll, mais getElement(s)By* peut être de temps en temps utile ou rencontrée dans de vieux scripts.
A part ça :
- Il y a
elem.matches(css)qui vérifie sielemcorrespond au sélecteur CSS donné. - Il y a
elem.closest(css)qui va chercher lâancêtre le plus proche qui correspond au sélecteur CSS donné.
Et on peut mentionner ici une autre méthode pour vérifier la relation parent-enfant, ce qui est parfois utile :
elemA.contains(elemB)renvoie true sielemBest danselemA(un descendant deelemA) ou quandelemA==elemB.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâ¦)