Cours JavaScript et TypeScript · Module 8: Événements et DOM

Les écouteurs d'événements - écouter les événements

11 min de lecture
Dans cette leçon7

Ray Arnold t'emmène plus profondément dans le centre de contrôle. « C'est ici que tout commence », pointe-t-il un immense panneau couvert de milliers de boutons et d'indicateurs. « Chaque élément de ce système est un écouteur d'événement (event listener) - un guetteur qui attend un signal. »

« À Jurassic Park », poursuit-il, « on a des capteurs de mouvement, des détecteurs de température, des caméras, des micros. Chacun écoute en permanence, attendant quelque chose d'inhabituel. Quand il détecte quelque chose, il réagit immédiatement. En JavaScript, on a addEventListener() - une méthode qui fonctionne exactement de la même manière. »

addEventListener() - la méthode fondamentale

La méthode addEventListener() est la base de la gestion des événements en JavaScript moderne. Elle permet d'attacher une fonction gestionnaire à un type d'événement précis sur un élément donné.

Syntaxe

1element.addEventListener(eventType, handlerFunction, options);
  • eventType - le type d'événement (chaîne) : 'click', 'keydown', 'submit', etc.
  • handlerFunction - la fonction appelée lorsque l'événement se produit
  • options - paramètres facultatifs (on y reviendra plus tard)

Exemple de base

« Commençons par quelque chose de simple », dit Ray. « Le bouton de nourrissage des vélociraptors. »

1// Récupérer l'élément bouton
2const feedButton = document.getElementById('velociraptor-feed');
3
4// Enregistrer un écouteur
5feedButton.addEventListener('click', function(event) {
6  console.log('Démarrage de la procédure de nourrissage des vélociraptors');
7  console.log('Cliqué sur :', event.target);
8
9  // Logique de nourrissage
10  feedVelociraptors();
11});
12
13function feedVelociraptors() {
14  console.log('Cage de sécurité : VERROUILLÉE');
15  console.log('Distributeur de nourriture : ACTIF');
16  console.log('Surveillance : ACTIVÉE');
17  console.log('Procédure terminée avec succès');
18}

Plusieurs écouteurs sur le même élément

« Et si on a besoin de plusieurs réactions au même événement ? », demande Ray. « Dans les systèmes du parc, c'est normal : un seul événement peut déclencher plusieurs protocoles. »

1const emergencyButton = document.getElementById('emergency');
2
3// Premier écouteur - activation de l'alarme
4emergencyButton.addEventListener('click', function() {
5  console.log('ALARME : activation des sirènes');
6  activateSirens();
7});
8
9// Deuxième écouteur - fermeture des portails
10emergencyButton.addEventListener('click', function() {
11  console.log('SÉCURITÉ : fermeture de tous les portails');
12  lockAllGates();
13});
14
15// Troisième écouteur - notification du personnel
16emergencyButton.addEventListener('click', function() {
17  console.log('COMMUNICATIONS : envoi des notifications à l\'équipe');
18  notifySecurityTeam();
19});
20
21// Les trois fonctions s'exécuteront au clic

Différents types de fonctions gestionnaires

Ray te montre différentes façons de définir des gestionnaires :

1const enclosureGate = document.getElementById('gate-5');
2
3// 1. Fonction anonyme
4enclosureGate.addEventListener('click', function(event) {
5  console.log('Portail cliqué', event.target.id);
6});
7
8// 2. Fonction fléchée (JavaScript moderne)
9enclosureGate.addEventListener('click', (event) => {
10  console.log('Portail cliqué', event.target.id);
11});
12
13// 3. Fonction nommée (bonne pratique)
14function handleGateClick(event) {
15  console.log('Portail cliqué', event.target.id);
16
17  if (event.target.dataset.status === 'open') {
18    closeGate(event.target.id);
19  } else {
20    openGate(event.target.id);
21  }
22}
23
24enclosureGate.addEventListener('click', handleGateClick);

« Les fonctions nommées sont les meilleures », explique Ray, « parce qu'on peut les supprimer plus tard, les tester séparément, et le code est plus lisible. »

L'objet Event - un rapport d'événement complet

« Quand un capteur détecte un mouvement », Ray montre un rapport à l'écran, « on reçoit un paquet de données complet : où, quand, quel type de mouvement, à quelle vitesse. L'objet Event en JavaScript fonctionne de la même façon. »

Propriétés de base

1document.addEventListener('click', function(event) {
2  // Informations de base sur l'événement
3  console.log('Type d\'événement :', event.type); // 'click'
4  console.log('Horodatage :', event.timeStamp); // temps en ms depuis le chargement de la page
5
6  // L'élément qui était la cible de l'événement
7  console.log('Élément cible :', event.target);
8  console.log('Élément courant :', event.currentTarget);
9
10  // Informations de position (pour les événements souris)
11  console.log('Position écran - X :', event.screenX, 'Y :', event.screenY);
12  console.log('Position fenêtre - X :', event.clientX, 'Y :', event.clientY);
13  console.log('Position page - X :', event.pageX, 'Y :', event.pageY);
14
15  // Touches de modification
16  console.log('Shift :', event.shiftKey);
17  console.log('Ctrl :', event.ctrlKey);
18  console.log('Alt :', event.altKey);
19  console.log('Meta (Cmd/Win) :', event.metaKey);
20
21  // Bouton de la souris
22  console.log('Bouton :', event.button); // 0=gauche, 1=milieu, 2=droit
23});

Utilisation pratique des propriétés

« Voyons cela dans un vrai système », dit Ray en ouvrant le code de la carte interactive du parc.

1const parkMap = document.getElementById('interactive-park-map');
2
3parkMap.addEventListener('click', function(event) {
4  // Récupérer les coordonnées du clic
5  const x = event.clientX;
6  const y = event.clientY;
7
8  // Vérifier si Ctrl était enfoncé (sélection multiple)
9  if (event.ctrlKey) {
10    console.log('Sélection multiple - ajout à la sélection');
11    addToSelection(event.target);
12  } else {
13    console.log('Sélection simple - effacement de la sélection précédente');
14    clearSelection();
15    selectSingle(event.target);
16  }
17
18  // click n'arrive que pour le bouton gauche (principal) de la souris,
19  // le bouton droit est géré plus bas par l'événement contextmenu
20  showEnclosureDetails(event.target);
21
22  // Sauvegarder la position pour une utilisation ultérieure
23  lastClickPosition = { x, y };
24});
25
26// Bloquer le menu contextuel par défaut (bouton droit)
27parkMap.addEventListener('contextmenu', function(event) {
28  event.preventDefault();
29  console.log('Menu contextuel personnalisé au lieu de celui par défaut');
30  showContextMenu(event.clientX, event.clientY);
31});

Différents types d'événements

Ray te montre d'autres écrans avec différents systèmes. « Chaque type de capteur détecte quelque chose de différent. De la même façon, on a différents types d'événements. »

Événements souris

1const dinoCard = document.getElementById('trex-card');
2
3// Clics
4dinoCard.addEventListener('click', (e) => {
5  console.log('Clic simple');
6  showBasicInfo();
7});
8
9dinoCard.addEventListener('dblclick', (e) => {
10  console.log('Double clic');
11  showDetailedView();
12});
13
14// Entrée et sortie
15dinoCard.addEventListener('mouseenter', (e) => {
16  console.log('La souris est entrée sur la carte');
17  e.target.classList.add('highlighted');
18  showQuickPreview();
19});
20
21dinoCard.addEventListener('mouseleave', (e) => {
22  console.log('La souris a quitté la carte');
23  e.target.classList.remove('highlighted');
24  hideQuickPreview();
25});
26
27// Mouvement de la souris (à utiliser avec prudence - peut générer des centaines d'événements)
28dinoCard.addEventListener('mousemove', (e) => {
29  // Mettre à jour la position de l'infobulle
30  updateTooltipPosition(e.clientX, e.clientY);
31});
32
33// Pression et relâchement d'un bouton
34dinoCard.addEventListener('mousedown', (e) => {
35  console.log('Bouton de la souris pressé');
36  startDrag(e);
37});
38
39dinoCard.addEventListener('mouseup', (e) => {
40  console.log('Bouton de la souris relâché');
41  endDrag(e);
42});

Événements clavier

« Beaucoup de systèmes se pilotent au clavier », explique Ray. « Codes d'accès, raccourcis clavier, navigation. »

1// Écouter sur tout le document
2document.addEventListener('keydown', function(event) {
3  console.log('Touche pressée :', event.key);
4  console.log('Code de la touche :', event.code);
5  console.log('Est-ce une répétition :', event.repeat);
6
7  // Raccourcis clavier
8  if (event.ctrlKey && event.key === 's') {
9    event.preventDefault(); // Bloquer l'enregistrement par défaut du navigateur
10    saveParkConfiguration();
11    console.log('Configuration enregistrée');
12  }
13
14  if (event.key === 'Escape') {
15    closeAllModals();
16    console.log('Toutes les fenêtres fermées');
17  }
18
19  // Navigation avec les touches fléchées
20  switch(event.key) {
21    case 'ArrowUp':
22      moveCamera('north');
23      break;
24    case 'ArrowDown':
25      moveCamera('south');
26      break;
27    case 'ArrowLeft':
28      moveCamera('west');
29      break;
30    case 'ArrowRight':
31      moveCamera('east');
32      break;
33  }
34});
35
36// Écouter sur un champ précis
37const securityCodeInput = document.getElementById('security-code');
38
39securityCodeInput.addEventListener('keydown', function(event) {
40  // Uniquement des chiffres ; les touches de contrôle (Retour arrière, flèches, Tab) ont des noms plus longs et passent
41  if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && (event.key < '0' || event.key > '9')) {
42    event.preventDefault();
43    console.log('Seuls les chiffres sont autorisés');
44  }
45});

Événements de formulaire

« Les formulaires sont une partie critique des systèmes du parc », dit Ray. « Signalement d'incidents, enregistrement des visiteurs, surveillance. »

1const incidentForm = document.getElementById('incident-report');
2const severitySelect = document.getElementById('severity');
3const descriptionArea = document.getElementById('description');
4
5// Submit - envoi du formulaire
6incidentForm.addEventListener('submit', function(event) {
7  event.preventDefault(); // IMPORTANT : bloquer l'envoi par défaut
8
9  console.log('Formulaire envoyé');
10
11  // Collecter les données
12  const formData = new FormData(incidentForm);
13  const data = {
14    severity: formData.get('severity'),
15    location: formData.get('location'),
16    description: formData.get('description'),
17    reporter: formData.get('reporter'),
18    timestamp: new Date().toISOString()
19  };
20
21  // Validation
22  if (validateIncidentReport(data)) {
23    submitIncidentReport(data);
24    incidentForm.reset();
25  } else {
26    console.error('Données de formulaire invalides');
27  }
28});
29
30// Change - valeur modifiée (après avoir quitté le champ)
31severitySelect.addEventListener('change', function(event) {
32  const severity = event.target.value;
33  console.log('Niveau de menace changé en :', severity);
34
35  // Ajuster la priorité et la couleur
36  if (severity === 'critical') {
37    incidentForm.classList.add('critical');
38    setPriority('HIGH');
39  } else {
40    incidentForm.classList.remove('critical');
41    setPriority('NORMAL');
42  }
43});
44
45// Input - valeur en train de changer (en temps réel)
46descriptionArea.addEventListener('input', function(event) {
47  const length = event.target.value.length;
48  const maxLength = 500;
49
50  console.log(`Caractères : ${length}/${maxLength}`);
51  updateCharacterCounter(length, maxLength);
52
53  if (length > maxLength) {
54    event.target.classList.add('error');
55  } else {
56    event.target.classList.remove('error');
57  }
58});
59
60// Focus et Blur - activation et désactivation d'un champ
61descriptionArea.addEventListener('focus', function(event) {
62  console.log('Champ de description actif - affichage des conseils');
63  showHelpTips();
64});
65
66descriptionArea.addEventListener('blur', function(event) {
67  console.log('Champ de description inactif - masquage des conseils');
68  hideHelpTips();
69});

removeEventListener() - supprimer les écouteurs

« Les systèmes qui ne sont plus nécessaires », Ray montre un panneau désactivé, « doivent être éteints. Ça économise l'énergie et les ressources. »

Suppression de base

1// Pour la supprimer, il faut la même fonction : nommée ou enregistrée dans une variable
2function handleClick(event) {
3  console.log('Cliqué');
4}
5
6const button = document.getElementById('temp-button');
7
8// Ajouter l'écouteur
9button.addEventListener('click', handleClick);
10
11// Le supprimer plus tard (doit être la même fonction)
12button.removeEventListener('click', handleClick);
13
14// Ça NE MARCHERA PAS - ce sont deux fonctions différentes
15button.addEventListener('click', () => console.log('Test'));
16button.removeEventListener('click', () => console.log('Test'));

Exemple pratique - surveillance temporisée

1function setupMaintenanceMode() {
2  console.log('Activation du mode maintenance pour 5 minutes');
3
4  const maintenanceAlert = function(event) {
5    event.preventDefault();
6    alert('Le parc est en mode maintenance. Merci de réessayer plus tard.');
7  };
8
9  // Bloquer toutes les actions
10  document.addEventListener('click', maintenanceAlert, true); // true = capture
11
12  // Au bout de 5 minutes, désactiver le mode maintenance
13  setTimeout(() => {
14    document.removeEventListener('click', maintenanceAlert, true);
15    console.log('Mode maintenance désactivé');
16  }, 5 * 60 * 1000);
17}

Les options d'addEventListener()

Ray te montre le panneau de configuration avancée. « Parfois, on a besoin de réglages spéciaux pour les capteurs. »

1element.addEventListener(eventType, handler, {
2  capture: false,  // Utiliser la phase de capture au lieu du bouillonnement
3  once: false,     // Supprimer l'écouteur après le premier appel
4  passive: false   // Ne pas appeler preventDefault()
5});

Exemple : once - écouteur à usage unique

1const welcomeScreen = document.getElementById('welcome');
2
3// Afficher le message d'accueil une seule fois
4welcomeScreen.addEventListener('click', function() {
5  console.log('Bienvenue à Jurassic Park !');
6  showTour();
7}, { once: true });
8
9// Après le premier clic, l'écouteur est automatiquement supprimé

Exemple : passive - un défilement tactile fluide

Un écouteur qui suit seulement le doigt et ne bloque jamais le défilement peut le promettre au navigateur avec l'option passive.

1// passive: true promet que l'écouteur n'appellera pas preventDefault(),
2// donc le navigateur fait défiler la page tout de suite, sans attendre l'écouteur
3window.addEventListener('touchmove', function(event) {
4  trackFinger(event.touches[0]);
5  // On ne peut pas utiliser event.preventDefault() avec passive: true
6}, { passive: true });

L'option compte pour touchstart, touchmove et wheel, car ces événements peuvent arrêter le défilement. Un événement scroll ne peut pas être annulé, donc là passive n'accélère rien.

Un système de surveillance en pratique

« Combinons tout cela en un seul système », dit Ray en ouvrant le code principal de surveillance.

1class ParkMonitoringSystem {
2  constructor() {
3    this.alerts = [];
4    this.isActive = false;
5    this.initialize();
6  }
7
8  initialize() {
9    console.log('Initialisation du système de surveillance...');
10
11    // Événements du document
12    document.addEventListener('DOMContentLoaded', () => {
13      this.onSystemReady();
14    });
15
16    // Événements de la fenêtre
17    window.addEventListener('beforeunload', (e) => {
18      return this.onBeforeExit(e);
19    });
20
21    // Événements d'erreur
22    window.addEventListener('error', (e) => {
23      this.logError(e);
24    });
25
26    this.isActive = true;
27  }
28
29  onSystemReady() {
30    console.log('Système de surveillance prêt');
31    this.setupEnclosureMonitors();
32    this.setupSecuritySystem();
33    this.startPeriodicChecks();
34  }
35
36  setupEnclosureMonitors() {
37    const enclosures = document.querySelectorAll('.enclosure');
38
39    enclosures.forEach(enclosure => {
40      // Statut de l'enclos
41      enclosure.addEventListener('click', (e) => {
42        this.showEnclosureStatus(enclosure.id);
43      });
44
45      // Détails au double clic
46      enclosure.addEventListener('dblclick', (e) => {
47        this.showDetailedView(enclosure.id);
48      });
49
50      // Mise en surbrillance au survol
51      enclosure.addEventListener('mouseenter', (e) => {
52        e.target.classList.add('active');
53        this.showQuickInfo(enclosure.id);
54      });
55
56      enclosure.addEventListener('mouseleave', (e) => {
57        e.target.classList.remove('active');
58        this.hideQuickInfo();
59      });
60    });
61  }
62
63  setupSecuritySystem() {
64    // Raccourcis clavier
65    document.addEventListener('keydown', (e) => {
66      if (e.ctrlKey && e.shiftKey && e.key === 'E') {
67        this.triggerEmergency();
68      }
69
70      if (e.key === 'F1') {
71        e.preventDefault();
72        this.showHelp();
73      }
74    });
75
76    // Bouton d'urgence
77    const emergencyBtn = document.getElementById('emergency');
78    if (emergencyBtn) {
79      emergencyBtn.addEventListener('click', () => {
80        this.triggerEmergency();
81      });
82    }
83  }
84
85  startPeriodicChecks() {
86    // Vérifier tous les systèmes toutes les 30 secondes
87    this.checkInterval = setInterval(() => {
88      if (this.isActive) {
89        this.performSystemCheck();
90      }
91    }, 30000);
92  }
93
94  performSystemCheck() {
95    console.log(`[${new Date().toLocaleTimeString()}] Vérification des systèmes...`);
96    // Logique de vérification
97  }
98
99  triggerEmergency() {
100    console.error('ALARME D\'URGENCE !');
101    this.addAlert({
102      type: 'emergency',
103      message: 'Alarme d\'urgence activée',
104      timestamp: new Date()
105    });
106  }
107
108  addAlert(alert) {
109    this.alerts.push(alert);
110    const event = new CustomEvent('parkAlert', { detail: alert });
111    document.dispatchEvent(event);
112  }
113
114  onBeforeExit(event) {
115    if (this.alerts.some(a => a.type === 'emergency')) {
116      event.returnValue = 'Il y a des alertes actives ! Es-tu sûr de vouloir quitter ?';
117      return event.returnValue;
118    }
119  }
120
121  logError(event) {
122    console.error('Erreur système :', event.error);
123    this.addAlert({
124      type: 'error',
125      message: event.error.message,
126      timestamp: new Date()
127    });
128  }
129
130  destroy() {
131    // Désactiver la surveillance
132    this.isActive = false;
133    clearInterval(this.checkInterval);
134    console.log('Système de surveillance désactivé');
135  }
136}
137
138// Initialisation
139const parkMonitoring = new ParkMonitoringSystem();

Récapitulatif

« Les écouteurs d'événements sont le cœur de tout système interactif », résume Ray. « Tout comme les capteurs de Jurassic Park veillent sur la sécurité, les écouteurs d'événements veillent sur le fonctionnement de ton application. »

Règles clés :

  • Utilise addEventListener() pour tous les écouteurs
  • Vérifie toujours que l'élément existe avant d'ajouter un écouteur
  • Garde les écouteurs que tu devras supprimer dans une fonction nommée ou une variable
  • N'oublie pas removeEventListener() quand un écouteur n'est plus nécessaire
  • Utilise l'option { once: true } pour les opérations à usage unique
  • Utilise { passive: true } sur touchstart, touchmove et wheel quand l'écouteur n'appelle pas preventDefault()
Code de cette leçon : index.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Galerie de dinosaures - Délégation d'événements</title>
7    <style>
8        body {
9            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10            background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
11            margin: 0;
12            padding: 20px;
13            color: white;
14        }
15
16        .gallery-container {
17            max-width: 800px;
18            margin: 0 auto;
19        }
20
21        h1 {
22            text-align: center;
23            margin-bottom: 30px;
24        }
25
26        .dinosaur-gallery {
27            display: grid;
28            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
29            gap: 20px;
30        }
31
32        .dino-card {
33            background: rgba(255, 255, 255, 0.1);
34            border-radius: 10px;
35            padding: 20px;
36            text-align: center;
37            cursor: pointer;
38            transition: all 0.3s ease;
39            border: 2px solid transparent;
40        }
41
42        .dino-card:hover {
43            transform: translateY(-5px);
44            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
45            border-color: #3498db;
46            background: rgba(255, 255, 255, 0.15);
47        }
48
49        .dino-card h3 {
50            margin: 10px 0;
51            color: #ecf0f1;
52        }
53
54        .dino-emoji {
55            font-size: 48px;
56            margin-bottom: 10px;
57            display: block;
58        }
59
60        .dino-details {
61            font-size: 14px;
62            color: #bdc3c7;
63            margin: 10px 0;
64        }
65
66        .action-buttons {
67            margin-top: 15px;
68        }
69
70        .details-btn, .favorite-btn {
71            background: #3498db;
72            color: white;
73            border: none;
74            padding: 8px 15px;
75            border-radius: 5px;
76            cursor: pointer;
77            margin: 0 5px;
78            transition: all 0.3s ease;
79        }
80
81        .details-btn:hover {
82            background: #2980b9;
83        }
84
85        .favorite-btn {
86            background: #e74c3c;
87        }
88
89        .favorite-btn:hover {
90            background: #c0392b;
91        }
92
93        .favorite-btn.active {
94            background: #f39c12;
95        }
96
97        .info-panel {
98            background: rgba(0, 0, 0, 0.8);
99            border-radius: 10px;
100            padding: 20px;
101            margin-top: 30px;
102            border: 1px solid #34495e;
103        }
104
105        .clicked-info {
106            color: #3498db;
107            font-weight: bold;
108        }
109    </style>
110</head>
111<body>
112    <div class="gallery-container">
113        <h1>Galerie de dinosaures de Jurassic Park</h1>
114
115        <!-- We use un seul event listener sur le conteneur -->
116        <div id="dino-gallery" class="dinosaur-gallery">
117            <div class="dino-card" data-dino-id="trex" data-dino-name="Tyrannosaurus Rex">
118                <span class="dino-emoji">◆</span>
119                <h3>Tyrannosaurus Rex</h3>
120                <div class="dino-details">
121                    Era: Crétacé<br>
122                    Diet: Carnivore<br>
123                    Length: 12-13m
124                </div>
125                <div class="action-buttons">
126                    <button class="details-btn">Détails</button>
127                    <button class="favorite-btn">Favoris</button>
128                </div>
129            </div>
130
131            <div class="dino-card" data-dino-id="velociraptor" data-dino-name="Velociraptor">
132                <span class="dino-emoji">◆</span>
133                <h3>Velociraptor</h3>
134                <div class="dino-details">
135                    Era: Crétacé<br>
136                    Diet: Carnivore<br>
137                    Length: 2m
138                </div>
139                <div class="action-buttons">
140                    <button class="details-btn">Détails</button>
141                    <button class="favorite-btn">Favoris</button>
142                </div>
143            </div>
144
145            <div class="dino-card" data-dino-id="triceratops" data-dino-name="Triceratops">
146                <span class="dino-emoji">◆</span>
147                <h3>Triceratops</h3>
148                <div class="dino-details">
149                    Era: Crétacé<br>
150                    Diet: Herbivore<br>
151                    Length: 9m
152                </div>
153                <div class="action-buttons">
154                    <button class="details-btn">Détails</button>
155                    <button class="favorite-btn">Favoris</button>
156                </div>
157            </div>
158
159            <div class="dino-card" data-dino-id="brachiosaurus" data-dino-name="Brachiosaurus">
160                <span class="dino-emoji">◆</span>
161                <h3>Brachiosaurus</h3>
162                <div class="dino-details">
163                    Era: Jurassique<br>
164                    Diet: Herbivore<br>
165                    Length: 23m
166                </div>
167                <div class="action-buttons">
168                    <button class="details-btn">Détails</button>
169                    <button class="favorite-btn">Favoris</button>
170                </div>
171            </div>
172        </div>
173
174        <div class="info-panel">
175            <h3>Panneau d'information</h3>
176            <div id="info-display">Click on a dinosaur card to see details...</div>
177        </div>
178    </div>
179
180    <script>
181        // Get references to elements
182        const dinoGallery = document.getElementById('dino-gallery');
183        const infoDisplay = document.getElementById('info-display');
184
185        // UN SEUL event listener sur le conteneur - Event Delegation !
186        dinoGallery.addEventListener('click', function(event) {
187            // Check what was clicked
188
189            // Handle click on dinosaur card
190            if (event.target.closest('.dino-card')) {
191                const card = event.target.closest('.dino-card');
192                const dinoId = card.dataset.dinoId;
193                const dinoName = card.dataset.dinoName;
194
195                console.log('Clicked dinosaur card:', dinoName, '(ID:', dinoId + ')');
196
197                // Mise à jour du panneau d'information
198                infoDisplay.innerHTML = `
199                    <span class="clicked-info">Sélectionné: ${dinoName}</span><br>
200                    ID: ${dinoId}<br>
201                    Click time: ${new Date().toLocaleTimeString('fr-FR')}
202                `;
203            }
204
205            // Handle details button
206            if (event.target.classList.contains('details-btn')) {
207                event.stopPropagation(); // Stop propagation
208
209                const card = event.target.closest('.dino-card');
210                const dinoName = card.dataset.dinoName;
211
212                alert(`Detailed information about ${dinoName}\n\nSpecimen is in enclosure no. ${Math.floor(Math.random() * 10) + 1}\nHealth status: Excellent\nDernier repas: ${new Date().toLocaleTimeString('fr-FR')}`);
213
214                infoDisplay.innerHTML = `
215                    <span class="clicked-info">Displayed details: ${dinoName}</span><br>
216                    Action: Details button<br>
217                    Heure: ${new Date().toLocaleTimeString('fr-FR')}
218                `;
219            }
220
221            // Handle favorites button
222            if (event.target.classList.contains('favorite-btn')) {
223                event.stopPropagation(); // Stop propagation
224
225                const card = event.target.closest('.dino-card');
226                const dinoName = card.dataset.dinoName;
227                const favoriteBtn = event.target;
228
229                // Toggle favoris
230                favoriteBtn.classList.toggle('active');
231
232                if (favoriteBtn.classList.contains('active')) {
233                    favoriteBtn.textContent = '★ Favoris';
234                    infoDisplay.innerHTML = `
235                        <span class="clicked-info">Ajouté aux favoris: ${dinoName}</span><br>
236                        Action: Ajout aux favoris<br>
237                        Heure: ${new Date().toLocaleTimeString('fr-FR')}
238                    `;
239                } else {
240                    favoriteBtn.textContent = 'Favoris';
241                    infoDisplay.innerHTML = `
242                        <span class="clicked-info">Removed from favorites: ${dinoName}</span><br>
243                        Action: Removed from favorites<br>
244                        Heure: ${new Date().toLocaleTimeString('fr-FR')}
245                    `;
246                }
247            }
248        });
249
250        // Démonstration des avantages de l'event delegation
251        function addNewDinosaur() {
252            const newDino = document.createElement('div');
253            newDino.className = 'dino-card';
254            newDino.setAttribute('data-dino-id', 'stegosaurus');
255            newDino.setAttribute('data-dino-name', 'Stegosaurus');
256            newDino.innerHTML = `
257                <span class="dino-emoji">◆</span>
258                <h3>Stegosaurus</h3>
259                <div class="dino-details">
260                    Era: Jurassique<br>
261                    Diet: Herbivore<br>
262                    Length: 9m
263                </div>
264                <div class="action-buttons">
265                    <button class="details-btn">Détails</button>
266                    <button class="favorite-btn">Favoris</button>
267                </div>
268            `;
269
270            dinoGallery.appendChild(newDino);
271
272            infoDisplay.innerHTML = `
273                <span class="clicked-info">Nouveau dinosaure ajouté: Stegosaurus</span><br>
274                Event delegation automatically handles new elements!<br>
275                Heure: ${new Date().toLocaleTimeString('fr-FR')}
276            `;
277        }
278
279        // Ajout d'un nouveau dinosaure après 3 secondes
280        setTimeout(addNewDinosaur, 3000);
281    </script>
282</body>
283</html>

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Que faut-il pour supprimer correctement un écouteur d'événement ?

Tâches pratiques dans le jeu

  • Éditeur de code

    Le fichier index.html contient un bouton d'alarme #alarm-btn, un paragraphe #alarm-status et un script dans une balise <script>. Complète les blancs : ___BLANK1___ est la méthode du bouton qui enregistre un écouteur d'événement, et ___BLANK2___ est le nom de l'événement de clic. Au clic sur le bouton, l'écouteur doit écrire le message d'alarme dans le paragraphe #alarm-status. Clique sur le bouton dans l'aperçu pour vérifier que le message apparaît.

  • Clique dans l'ordre

    Place les éléments dans le bon ordre pour créer un écouteur d'événement.

  • Clique dans l'ordre

    Place les éléments de l'ajout d'un écouteur d'événement avec l'option once :

Articles utiles