🌐 AIæœçŽą & 代理 䞻饔
18 octobre 2023

Recherches: getElement*, querySelector*

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>
Ne pas utiliser les variables globales nommĂ©es selon l’id pour accĂ©der aux Ă©lĂ©ments !

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.

L’ id doit ĂȘtre unique

L’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.

Seulement document.getElementById, pas anyElem.getElementById

La 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.

On peut aussi utiliser des pseudo-classes

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Ăštre tag peut 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’attribut name, 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>
N’oubliez pas la lettre "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".

Elle renvoie un ensemble, pas un élément !

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 :

  1. Le premier crĂ©e une rĂ©fĂ©rence Ă  l’ensemble des <div>. Maintenant, sa longueur est 1.
  2. Le second se lance aprÚs que le navigateur aie rencontré un autre <div>, donc sa longueur est 2.
<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 si elem correspond 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 si elemB est dans elemA (un descendant de elemA) ou quand elemA==elemB.

Exercices

importance: 4

Voici le document avec le tableau et formulaire

Comment trouver ?


  1. Le tableau avec id="age-table".
  2. Tous les éléments label dans ce tableau (il devrait y en avoir 3).
  3. Le premier td dans ce tableau (avec le mot “Age”).
  4. Le form avec name="search".
  5. Le premier input dans ce formulaire.
  6. Le dernier input dans ce formulaire.

Ouvrez la page table.html dans un onglet Ă  part et utilisez les outils du navigateur pour cela.

Il existe plusieurs façons de faire : En voici quelques unes :

// 1. Le tableau avec `id="age-table"`.
let table = document.getElementById('age-table')

// 2. Tous les éléments 'label' dans le tableau
table.getElementsByTagName('label')
// ou
document.querySelectorAll('#age-table label')

// 3. Le premier td dans ce tableau (avec le mot "Age")
table.rows[0].cells[0]
// ou
table.getElementsByTagName('td')[0]
// ou
table.querySelector('td')

// 4. Le formulaire avec le nom "search"
// en supposant qu'il n'y ait qu'un élément avec name="search" dans le document.
let form = document.getElementsByName('search')[0]
// ou spécifiquement un formulaire
document.querySelector('form[name="search"]')

// 5. Le premier input dans ce formulaire
form.getElementsByTagName('input')[0]
// ou
form.querySelector('input')

// 6. Le dernier input dans ce formulaire
let inputs = form.querySelectorAll('input') // trouve tous les inputs
inputs[inputs.length-1] // prend le dernier
Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)