Javascript fondamentaux
Thème :
Intermédiaire
1 vues ·8/19 leçonsEvent Delegation (Délégation d’événements) en JavaScript
Concept fondamental
La délégation d’événements est une technique qui consiste à ajouter un seul écouteur d’événement au parent au lieu d’en ajouter un à chaque enfant. L’écouteur parent “capture” les clics sur tous les enfants grâce au bubbling (remontée d’événement).
Pourquoi utiliser la délégation d’événements ?
❌ Approche SANS délégation (Mauvaise)
// Si vous avez 100 éléments <li>, vous créez 100 écouteurs !
const items = document.querySelectorAll('li');
items.forEach((item) => {
item.addEventListener('click', () => {
console.log('Cliqué :', item.target.textContent);
});
});
Problèmes :
- ⚠️ Crée 100 écouteurs différents
- ⚠️ Consomme beaucoup de mémoire
- ⚠️ Si vous ajoutez un nouvel
<li>dynamiquement, il n’aura pas d’écouteur - ⚠️ Code lourd et inefficace
✅ Approche AVEC délégation (Bonne)
// Un seul écouteur sur le parent
const liste = document.querySelector('ul');
liste.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
console.log('Cliqué :', event.target.textContent);
}
});
Avantages :
- ✅ Un seul écouteur, peu importe le nombre d’enfants
- ✅ Économe en mémoire
- ✅ Fonctionne aussi pour les éléments ajoutés dynamiquement
- ✅ Code plus simple et performant
Comment fonctionne le Bubbling ?
Le bubbling (remontée d’événement) est le mécanisme qui permet à la délégation de fonctionner.
Schéma de propagation :
┌─────────────────────────────────┐
│ <ul> (parent) │ ← L'écouteur est ici
│ ┌───────────────────────────┐ │
│ │ <li>Élément 1</li> │ │ ← Vous cliquez ici
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ <li>Élément 2</li> │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
Flux de l'événement :
1. Clic sur <li> (target)
2. Remonte vers <ul> (parent)
3. L'écouteur du parent capture l'événement
Détail du code : Méthode 1 avec tagName
const liste = document.querySelector('ul');
liste.addEventListener('click', (event) => {
// Vérifier si c'est un <li>
if (event.target.tagName === 'LI') {
console.log('Élément cliqué :', event.target.textContent);
}
});
Explication ligne par ligne :
| Code | Explication |
|---|---|
liste.addEventListener('click', ...) |
Ajoute un écouteur au parent <ul> |
event.target |
L’élément exactement cliqué (peut être <li> ou un enfant du <li>) |
event.target.tagName |
Le nom de la balise HTML (ex: ‘LI’, ‘SPAN’, ‘BUTTON’) |
=== 'LI' |
Vérifie si c’est bien un élément <li> |
event.target.textContent |
Récupère le texte de l’élément |
Exemple HTML :
<ul id="liste">
<li>Élément 1</li>
<li>Élément 2</li>
<li>Élément 3</li>
</ul>
Résultat :
Clic sur "Élément 1" → Affiche : "Élément cliqué : Élément 1"
Clic sur "Élément 2" → Affiche : "Élément cliqué : Élément 2"
Clic sur "Élément 3" → Affiche : "Élément cliqué : Élément 3"
Détail du code : Méthode 2 avec closest() (Recommandée)
liste.addEventListener('click', (event) => {
// Chercher l'ancêtre <li> le plus proche
const item = event.target.closest('li');
if (item) {
console.log('Élément :', item.textContent);
}
});
Qu’est-ce que closest() ?
closest() remonte l’arborescence DOM jusqu’à trouver un élément correspondant au sélecteur.
Comparaison : tagName vs closest()
Problème avec tagName :
<ul>
<li><strong>Texte en gras</strong> ← Vous cliquez ici</li>
</ul>
liste.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
console.log('Cliqué sur LI');
}
});
// ❌ Ne s'affiche PAS car event.target est <strong>, pas <li>
Solution avec closest() :
liste.addEventListener('click', (event) => {
const item = event.target.closest('li');
if (item) {
console.log('Cliqué sur LI ou ses enfants');
}
});
// ✅ S'affiche car closest() remonte jusqu'à <li>
Fonctionnement détaillé de closest()
const item = event.target.closest('li');
Étapes :
1. event.target = <strong>Texte en gras</strong>
2. closest('li') cherche : "Es-tu un <li> ?" → NON
3. Remonte au parent : <li>
4. closest('li') cherche : "Es-tu un <li> ?" → OUI ✅
5. Retourne : <li>Texte en gras</li>
Cas d’usage pratiques
1. Liste de tâches dynamique
<ul id="todos">
<li>Tâche 1 <button class="delete">✕</button></li>
<li>Tâche 2 <button class="delete">✕</button></li>
<li>Tâche 3 <button class="delete">✕</button></li>
</ul>
const todoList = document.querySelector('#todos');
todoList.addEventListener('click', (event) => {
// Clic sur le bouton supprimer
if (event.target.classList.contains('delete')) {
const item = event.target.closest('li');
item.remove();
console.log('Tâche supprimée');
}
// Clic sur l'élément lui-même
if (event.target.tagName === 'LI') {
event.target.classList.toggle('completed');
}
});
// Ajouter une nouvelle tâche dynamiquement
const newItem = document.createElement('li');
newItem.textContent = 'Tâche 4';
newItem.innerHTML += ' <button class="delete">✕</button>';
todoList.appendChild(newItem);
// ✅ Fonctionne automatiquement sans ajouter d'écouteur !
2. Tableau avec actions
<table id="users">
<tr>
<td>Alice</td>
<td><button class="edit">Éditer</button></td>
</tr>
<tr>
<td>Bob</td>
<td><button class="edit">Éditer</button></td>
</tr>
</table>
const table = document.querySelector('#users');
table.addEventListener('click', (event) => {
if (event.target.classList.contains('edit')) {
const row = event.target.closest('tr');
const name = row.querySelector('td').textContent;
console.log('Éditer :', name);
}
});
3. Menu avec sous-éléments
<nav id="menu">
<ul>
<li class="item">
Accueil
<ul>
<li class="subitem">Sous-menu 1</li>
</ul>
</li>
</ul>
</nav>
const menu = document.querySelector('#menu');
menu.addEventListener('click', (event) => {
// Clic sur un élément de menu
const item = event.target.closest('.item');
if (item) {
console.log('Menu cliqué :', item.textContent);
}
// Clic sur un sous-élément
const subitem = event.target.closest('.subitem');
if (subitem) {
console.log('Sous-menu cliqué :', subitem.textContent);
}
});
Comparaison : tagName vs closest() vs matches()
| Méthode | Utilisation | Avantage |
|---|---|---|
tagName === 'LI' |
Vérifier la balise exacte | Simple, rapide |
closest('li') |
Chercher l’ancêtre | Fonctionne avec les enfants imbriqués |
matches('li') |
Vérifier le sélecteur actuel | Flexible avec les classes/IDs |
// Exemple avec matches()
liste.addEventListener('click', (event) => {
if (event.target.matches('li.active')) {
console.log('Élément actif cliqué');
}
});
Avantages résumés
| Aspect | Sans délégation | Avec délégation |
|---|---|---|
| Nombre d’écouteurs | 100 (pour 100 éléments) | 1 |
| Mémoire | Élevée | Basse |
| Éléments dynamiques | ❌ Pas de listener | ✅ Fonctionne |
| Performance | Lente | Rapide |
| Maintenabilité | Difficile | Facile |
Pièges à éviter
❌ Mauvais : Oublier de vérifier la cible
liste.addEventListener('click', (event) => {
// Cela s'exécute pour TOUS les clics, même sur <ul>
console.log('Cliqué !');
});
✅ Bon : Toujours vérifier
liste.addEventListener('click', (event) => {
const item = event.target.closest('li');
if (item) {
console.log('Cliqué sur un <li>');
}
});
Résumé
| Concept | Explication |
|---|---|
| Bubbling | L’événement remonte du enfant vers le parent |
| Délégation | Ajouter un écouteur au parent au lieu de chaque enfant |
event.target |
L’élément exactement cliqué |
closest() |
Chercher l’ancêtre correspondant au sélecteur |
tagName |
Vérifier le type de balise HTML |
La délégation d’événements est essentielle pour du code performant et maintenable ! 🚀
Prêt à passer à la suite ?
Vous avez terminé cette leçon sur Event Delegation (Délégation d'événements) en JavaScript. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.