Comprendre addEventListener en JavaScript
La méthode addEventListener est la pierre angulaire de l'interactivité web en JavaScript. Elle permet d'attacher des fonctions qui s'exécutent en réponse à des événements utilisateur : clics, saisie clavier, scroll, soumission de formulaire, et bien d'autres.
C'est la méthode moderne et recommandée pour gérer les événements, remplaçant les anciennes approches comme onclick ou les attributs HTML inline.
Syntaxe de addEventListener
// Syntaxe complète
element.addEventListener(type, listener, options);
// Exemples basiques
const button = document.querySelector('.btn');
// Avec une fonction nommée
function handleClick(event) {
console.log('Bouton cliqué !', event);
}
button.addEventListener('click', handleClick);
// Avec une fonction fléchée
button.addEventListener('click', (e) => {
console.log('Cliqué !', e.target);
});
// Avec une fonction anonyme
button.addEventListener('click', function(e) {
console.log('Cliqué !');
});
Les événements les plus utilisés
Événements de souris
| Événement | Déclenché quand |
|---|---|
click | Clic gauche (ou tap sur mobile) |
dblclick | Double clic |
mouseenter | Le curseur entre dans l'élément |
mouseleave | Le curseur quitte l'élément |
mousemove | Le curseur bouge sur l'élément |
mousedown | Bouton de souris enfoncé |
mouseup | Bouton de souris relâché |
contextmenu | Clic droit |
Événements de clavier
document.addEventListener('keydown', (e) => {
console.log('Touche pressée :', e.key, e.code);
// Raccourcis clavier
if (e.ctrlKey && e.key === 's') {
e.preventDefault(); // Empêche le comportement par défaut
saveDocument();
}
// Touche Escape
if (e.key === 'Escape') {
closeModal();
}
});
// keyup : quand la touche est relâchée
input.addEventListener('keyup', (e) => {
if (e.key === 'Enter') {
submitForm();
}
});
// input : à chaque modification de la valeur
searchInput.addEventListener('input', (e) => {
filterResults(e.target.value);
});
Événements de formulaire
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
e.preventDefault(); // Empêche le rechargement de la page
const formData = new FormData(form);
// Traitement des données
});
// Focus et blur
input.addEventListener('focus', () => {
input.classList.add('focused');
});
input.addEventListener('blur', () => {
input.classList.remove('focused');
validateField(input);
});
// Change (select, checkbox, radio)
select.addEventListener('change', (e) => {
console.log('Nouvelle valeur :', e.target.value);
});
Événements de scroll et resize
// Scroll (avec debounce pour les performances)
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(() => {
handleScroll(window.scrollY);
ticking = false;
});
ticking = true;
}
});
// Resize
window.addEventListener('resize', () => {
// Utiliser un debounce en production
adjustLayout();
});
L'objet Event en détail
button.addEventListener('click', (event) => {
// Propriétés communes
event.type; // 'click'
event.target; // L'élément qui a déclenché l'événement
event.currentTarget; // L'élément sur lequel le listener est attaché
event.timeStamp; // Timestamp de l'événement
// Méthodes importantes
event.preventDefault(); // Empêche le comportement par défaut
event.stopPropagation(); // Arrête la propagation (bubbling)
event.stopImmediatePropagation(); // Arrête aussi les autres listeners
// Propriétés de souris
event.clientX; // Position X dans la fenêtre
event.clientY; // Position Y dans la fenêtre
event.pageX; // Position X dans la page (avec scroll)
event.pageY; // Position Y dans la page
event.button; // 0=gauche, 1=milieu, 2=droite
// Modificateurs
event.ctrlKey; // Ctrl enfoncé
event.shiftKey; // Shift enfoncé
event.altKey; // Alt enfoncé
event.metaKey; // Cmd (Mac) / Win (Windows) enfoncé
});
Options avancées de addEventListener
// Troisième paramètre : options
element.addEventListener('click', handler, {
once: true, // Se déclenche une seule fois puis se supprime
passive: true, // Ne peut pas appeler preventDefault() - meilleure perf
capture: false, // Phase de capture (false = bubbling, par défaut)
signal: controller.signal // Pour annuler avec AbortController
});
// once: true - Parfait pour les modals, les one-time actions
button.addEventListener('click', () => {
showWelcomeMessage();
}, { once: true });
// passive: true - Recommandé pour scroll et touch
document.addEventListener('touchstart', handleTouch, { passive: true });
document.addEventListener('wheel', handleWheel, { passive: true });
// AbortController pour nettoyer les listeners
const controller = new AbortController();
element.addEventListener('click', handler, { signal: controller.signal });
element.addEventListener('keydown', handler2, { signal: controller.signal });
// Supprimer tous les listeners d'un coup
controller.abort();
Délégation d'événements
La délégation est un pattern essentiel qui attache un seul listener sur un parent au lieu d'un listener par enfant :
// MAUVAIS : un listener par élément
document.querySelectorAll('.card').forEach(card => {
card.addEventListener('click', handleCard); // 100 listeners !
});
// BON : délégation d'événements
document.querySelector('.card-grid').addEventListener('click', (e) => {
const card = e.target.closest('.card');
if (card) {
handleCard(card);
}
});
// Exemple concret : liste dynamique
const todoList = document.querySelector('.todo-list');
todoList.addEventListener('click', (e) => {
// Bouton supprimer
if (e.target.matches('.btn-delete')) {
e.target.closest('.todo-item').remove();
}
// Checkbox compléter
if (e.target.matches('.todo-checkbox')) {
e.target.closest('.todo-item').classList.toggle('completed');
}
});
Supprimer un écouteur d'événement
// removeEventListener nécessite une référence à la même fonction
function handleClick(e) {
console.log('Cliqué');
}
button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick); // OK
// ATTENTION : ceci NE FONCTIONNE PAS
button.addEventListener('click', (e) => console.log('A'));
button.removeEventListener('click', (e) => console.log('A')); // FAIL - fonction différente
// Solution moderne : AbortController
const ctrl = new AbortController();
button.addEventListener('click', (e) => {
console.log('Cliqué');
}, { signal: ctrl.signal });
// Plus tard, pour supprimer :
ctrl.abort();
Patterns pratiques courants
Debounce pour les recherches
function debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
const searchInput = document.querySelector('#search');
searchInput.addEventListener('input', debounce((e) => {
fetchSearchResults(e.target.value);
}, 300));
Throttle pour le scroll
function throttle(fn, limit = 100) {
let inThrottle;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
window.addEventListener('scroll', throttle(() => {
updateScrollProgress();
}, 50));
Détection de clic extérieur (fermer un dropdown)
const dropdown = document.querySelector('.dropdown');
const trigger = document.querySelector('.dropdown-trigger');
trigger.addEventListener('click', () => {
dropdown.classList.toggle('open');
});
document.addEventListener('click', (e) => {
if (!dropdown.contains(e.target) && !trigger.contains(e.target)) {
dropdown.classList.remove('open');
}
});
Bonnes pratiques
- Utilisez la délégation pour les listes dynamiques et les éléments nombreux.
- Nettoyez vos listeners avec removeEventListener ou AbortController quand les éléments sont supprimés.
- Utilisez passive: true pour les événements scroll et touch.
- Debounce les événements fréquents (input, resize, scroll) pour les performances.
- Préférez les fonctions nommées aux fonctions anonymes pour faciliter le débogage.
- N'utilisez jamais les attributs HTML inline (
onclick="") en production.
Conclusion
addEventListener est la méthode fondamentale pour toute interactivité web. Maîtrisez la délégation, les options avancées et les patterns de performance pour créer des interfaces réactives et robustes.
Chez Tourak Digital, nous développons des interfaces interactives performantes et accessibles. Contactez-nous pour votre projet web.