Un tableau de deux cents lignes, un bouton de suppression sur chacune. Poser deux cents écouteurs fonctionne, mais coûte de la mémoire, et ne couvre pas la ligne ajoutée trois secondes plus tard.
Il existe une solution qui tient en un seul écouteur, et elle repose entièrement sur la remontée des événements.
Définition
La délégation d'événement consiste à écouter un ancêtre commun plutôt que chaque élément, puis à identifier au moment du déclenchement l'élément réellement visé. Elle est possible parce qu'un événement remonte de sa cible jusqu'à la racine du document.
const tableau = document.querySelector("#commandes");
tableau.addEventListener("click", (event) => {
const bouton = event.target.closest(".supprimer");
if (!bouton) return;
const ligne = bouton.closest("tr");
ligne.remove();
});Un seul écouteur, quel que soit le nombre de lignes. Les lignes insérées plus tard sont couvertes sans le moindre code supplémentaire, puisque rien n'est branché sur elles.
closest, la pièce maîtresse
event.target désigne l'élément le plus profond touché par le geste, souvent une icône ou un texte à l'intérieur du bouton. Comparer directement ce nœud au sélecteur attendu échoue donc une fois sur deux.
closest remonte depuis un élément vers ses ancêtres et rend le premier qui correspond au sélecteur, l'élément de départ compris. Il rend null quand aucun ancêtre ne correspond, ce qui sert de filtre naturel.
// <button class="supprimer"><span class="icone"></span> Supprimer</button>
tableau.addEventListener("click", (event) => {
console.log(event.target.tagName); // "SPAN" parfois
console.log(event.target.closest(".supprimer")); // toujours le bouton
});Ce qu'elle apporte
- Le contenu dynamique fonctionne sans réenregistrement après chaque ajout de ligne.
- La mémoire ne grandit plus avec le nombre d'éléments affichés.
- Le nettoyage se réduit à un écouteur unique à retirer quand la vue disparaît.
L'attribut data-*, lu par dataset, complète l'ensemble : il transporte sur chaque ligne l'identifiant à envoyer au serveur, que l'écouteur récupère au moment du clic.
Ses limites
La technique ne s'applique qu'aux événements qui remontent. focus et blur restent sur leur cible : leurs jumeaux focusin et focusout, eux, remontent et se délèguent normalement.
Écoutez le conteneur le plus proche, pas document. Un écouteur global voit passer tous les clics de la page, et un appel à stopPropagation placé plus bas suffit à le rendre muet sans explication.
Questions fréquentes
Faut-il déléguer systématiquement ?
Non. Pour un bouton unique, un écouteur direct est plus court et plus lisible. La délégation prend l'avantage sur une collection d'éléments similaires, ou dès que le contenu change au fil de l'usage.
Comment gérer plusieurs actions dans le même conteneur ?
Un seul écouteur suffit encore. Testez les cibles possibles l'une après l'autre avec closest, ou lisez un attribut data-action puis orientez le traitement avec un switch. Cette seconde forme reste lisible même avec une dizaine d'actions.
Le clavier est-il couvert ?
Il l'est si le HTML est correct. Un vrai <button> déclenche un événement click à la touche Entrée comme à la barre d'espace, et cet événement remonte. Un <div> rendu cliquable, lui, n'offre rien de tel et exclut une partie des visiteurs.