Cours JavaScript et TypeScript · Module 8: Événements et DOM
Les écouteurs d'événements - écouter les événements
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 clicDiffé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 }surtouchstart,touchmoveetwheelquand l'écouteur n'appelle paspreventDefault()
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. 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 :