// Web

Les événements en JavaScript (addEventListener)

Les événements sont ce qui rend une page web vivante : un clic, une frappe au clavier, l’envoi d’un formulaire, le survol d’un bouton… À chaque interaction, le navigateur émet un événement que votre JavaScript peut écouter pour réagir. La méthode addEventListener est l’outil central de cette écoute. Ce guide couvre son fonctionnement, l’objet event, les types courants, la propagation entre éléments, l’annulation du comportement par défaut, la puissante technique de délégation, et le nettoyage des écouteurs.

addEventListener : écouter un événement

La méthode addEventListener s’attache à un élément du DOM. Elle prend le type d’événement (une chaîne comme "click") et une fonction de rappel (le callback) exécutée à chaque déclenchement.

const bouton = document.querySelector("#valider");

bouton.addEventListener("click", () => {
  console.log("Bouton cliqué !");
});

À chaque clic sur le bouton, la fonction s’exécute. On peut attacher plusieurs écouteurs au même élément et au même type sans qu’ils s’écrasent, ce qui est un gros avantage sur l’ancienne syntaxe element.onclick = ... (limitée à un seul gestionnaire). Manipuler ces éléments suppose de savoir les sélectionner dans le DOM, étape préalable à toute écoute.

L’objet event

Le callback reçoit automatiquement un argument : l’objet event, qui décrit ce qui s’est passé. Il contient des dizaines d’informations utiles selon le type d’événement.

bouton.addEventListener("click", (event) => {
  console.log(event.type);          // "click"
  console.log(event.target);        // l'élément cliqué
  console.log(event.clientX, event.clientY); // coordonnées du curseur
});

Deux propriétés reviennent constamment :

  • event.target : l’élément qui a réellement déclenché l’événement (celui sous le curseur, par exemple) ;
  • event.currentTarget : l’élément sur lequel l’écouteur est attaché (souvent un parent). La distinction devient cruciale avec la propagation, vue plus bas.

Les types d’événements courants

Il existe des centaines de types d’événements. Voici les familles les plus utilisées au quotidien.

// Souris
element.addEventListener("click", gerer);
element.addEventListener("dblclick", gerer);
element.addEventListener("mouseenter", gerer);
element.addEventListener("mouseleave", gerer);

// Clavier
input.addEventListener("keydown", gerer);
input.addEventListener("keyup", gerer);

// Formulaire
formulaire.addEventListener("submit", gerer);
input.addEventListener("input", gerer);   // à chaque frappe
select.addEventListener("change", gerer);  // après validation du choix

// Fenêtre et document
window.addEventListener("resize", gerer);
window.addEventListener("scroll", gerer);
document.addEventListener("DOMContentLoaded", gerer); // page prête

Exemple : lire une touche du clavier

Les événements clavier exposent la touche pressée via event.key.

const champ = document.querySelector("#recherche");

champ.addEventListener("keydown", (event) => {
  if (event.key === "Enter") {
    console.log("Recherche lancée :", champ.value);
  }
  if (event.key === "Escape") {
    champ.value = "";
  }
});

La propagation : bubbling et capture

Quand vous cliquez sur un élément imbriqué dans d’autres, l’événement ne concerne pas que lui. Il se propage. Par défaut, il suit la phase de bubbling (remontée) : il part de l’élément cliqué et remonte vers ses parents, jusqu’à document.

// HTML : <div id="carte"><button id="btn">Cliquer</button></div>

document.querySelector("#carte").addEventListener("click", () => {
  console.log("Div carte");
});

document.querySelector("#btn").addEventListener("click", () => {
  console.log("Bouton");
});

// Un clic sur le bouton affiche :
// "Bouton" puis "Div carte" — l'événement remonte

Il existe aussi une phase de capture (descente), qui va des parents vers la cible, activée en passant true (ou { capture: true }) en troisième argument. On l’utilise rarement, mais il faut connaître son existence.

document.querySelector("#carte").addEventListener("click", () => {
  console.log("Capture parent en premier");
}, true);

preventDefault et stopPropagation

Deux méthodes de l’objet event permettent de reprendre le contrôle. Elles sont souvent confondues alors qu’elles font des choses distinctes.

preventDefault() annule le comportement par défaut du navigateur : empêcher un lien de naviguer, un formulaire de recharger la page, une case de se cocher.

formulaire.addEventListener("submit", (event) => {
  event.preventDefault(); // pas de rechargement de page
  console.log("Envoi géré en JavaScript");
  // ... traitement, envoi via fetch, etc.
});

stopPropagation() arrête la propagation : l’événement ne remontera pas aux parents. Utile pour éviter qu’un clic sur un élément interne déclenche l’écouteur d’un conteneur.

document.querySelector("#btn").addEventListener("click", (event) => {
  event.stopPropagation(); // "Div carte" ne s'affichera plus
  console.log("Bouton seulement");
});

Retenez la distinction : preventDefault agit sur le navigateur, stopPropagation agit sur les autres écouteurs.

La délégation d’événements

Voici une technique aussi élégante qu’essentielle. Plutôt que d’attacher un écouteur à chaque élément d’une liste (coûteux, et inopérant pour les éléments ajoutés plus tard), on attache un seul écouteur au parent commun et on utilise event.target pour savoir quel enfant a été cliqué. C’est le bubbling qui rend cela possible.

const liste = document.querySelector("#taches");

liste.addEventListener("click", (event) => {
  // On ne réagit qu'aux clics sur un bouton .supprimer
  if (event.target.matches(".supprimer")) {
    event.target.closest("li").remove();
  }
});

Un seul écouteur gère tous les boutons de la liste, y compris ceux ajoutés dynamiquement après le chargement. La méthode matches() teste si l’élément cliqué correspond à un sélecteur, et closest() remonte jusqu’à l’ancêtre voulu. La délégation est la bonne réponse à « comment gérer les clics sur une liste qui change ».

removeEventListener : nettoyer un écouteur

Pour retirer un écouteur, on utilise removeEventListener. Contrainte majeure : il faut lui passer exactement la même fonction qu’à l’ajout. Une fonction anonyme est donc impossible à retirer — il faut une référence nommée.

function gererClic() {
  console.log("Clic !");
}

bouton.addEventListener("click", gererClic);

// Plus tard : on retire (même référence obligatoire)
bouton.removeEventListener("click", gererClic);

L’option { once: true } est un raccourci pratique quand l’écouteur ne doit se déclencher qu’une seule fois : il se retire automatiquement après le premier déclenchement.

bouton.addEventListener("click", () => {
  console.log("Ne s'exécute qu'une fois");
}, { once: true });

Nettoyer les écouteurs devenus inutiles évite les fuites de mémoire, notamment dans les applications à navigation dynamique où l’on crée et détruit beaucoup d’éléments.

À retenir

addEventListener(type, callback) est la porte d’entrée de toute interactivité : elle écoute un événement et exécute une fonction qui reçoit l’objet event. Retenez event.target (l’élément déclencheur) face à event.currentTarget (celui qui écoute), la remontée par bubbling qui permet la délégation, et les deux garde-fous preventDefault (annuler le comportement du navigateur) et stopPropagation (stopper la remontée). Privilégiez la délégation pour les listes dynamiques et pensez à retirer les écouteurs devenus inutiles avec removeEventListener ou l’option { once: true }.

À lire ensuite