Imaginons une liste d’ancres toute bête :
<ul id="ma-liste">
<li><a href="#">une ancre</a></li>
<li><a href="#">une autre ancre</a></li>
<li><a href="#">et encore une autre</a></li>
</ul>
Mais ces ancres, nous ne voulons pas de leur comportement habituel. Nous voulons qu’elles ouvrent une popin ou dieu sait quelle idée farfelue est sortie du cerveau collectif et surchauffé de notre team UX.
Avec jQuery, on pourrait être tenté de faire comme ça :
var $mes_ancres = $("#ma-liste a");
$mes_ancres.on("click", function(e){
e.preventDefault();
console.log($(this).html());
});
En goût-vanille, ça donnerait quelque chose comme ça :
var mes_ancres = document.querySelectorAll("#ma-liste a");
mes_ancres.forEach(function(element){
element.addEventListener("click", function(e){
e.preventDefault();
console.log(e.target.innerHTML);
}, false);
});
Peu importe la méthode, le résultat est le même dans les deux cas : un écouteur d’évènements et son gestionnaire sont ajoutés à chacune des ancres.
Il y a cependant un problème assez voyant avec cette approche. Nous avons trois gestionnaires d’évènements séparés mais complètement identiques associés à trois éléments du DOM. Ça prend de la place et c’est difficile à nettoyer.
Pas de soucis ! Il suffit d’extraire le gestionnaire anonyme et dans faire une vraie fonction :
function trucFarfelu(e) {
e.preventDefault();
console.log(e.target.innerHTML);
}
et de la passer en référence en jQuery :
var $mes_ancres = $("#ma-liste a");
$mes_ancres.on("click", trucFarfelu);
ou en goût-vanille :
var mes_ancres = document.querySelectorAll("#ma-liste a");
mes_ancres.forEach(function(element){
element.addEventListener("click", trucFarfelu, false);
});
On est bon, là.
Sauf qu’à un moment il y a un petit malin qui décide que l’utilisateur doit être en mesure d’ajouter des ancres à notre chère liste en cliquant quelque part dans la page.
Ça va donner quelque chose comme ça avec jQuery :
var $mon_ancre = $('<li><a href="#">une ancre de plus</a></li>');
$("#ma-liste").append($mon_ancre);
ou comme ça en goût-vanille :
var mon_ancre = document.createElement("li");
mon_ancre.innerHTML = '<a href="#">une ancre de plus</a>';
document.querySelector("#ma-liste").appendChild(mon_ancre);
Et là on est embêté car ça ne marche pas vraiment comme prévu : les nouvelles ancres se comportent pile-poil comme des ancres banales, sans faire quoi que ce soit de farfelu. Fouchtri de fouchtra !
Bah ! La solution est toute trouvée ; il suffit d’ajouter un écouteur d’évènement sur chaque nouvelle ancre, non ?
Allez, c’est parti en jQuery :
var $mon_ancre = $('<li><a href="#">une ancre de plus</a></li>');
$mon_ancre.on("click", trucFarfelu);
$("#ma-liste").append($mon_ancre);
et en goût-vanille :
var mon_ancre = document.createElement("li");
mon_ancre.innerHTML = '<a href="#">une ancre de plus</a>';
mon_ancre.querySelector("a").addEventListener("click", trucFarfelu, false);
document.querySelector("#ma-liste").appendChild(mon_ancre);
Normalement, c’est le moment où s'aperçoit que ce même écouteur d'évènement apparait deux fois dans notre code et qu'on se dit qu’on pourrait peut-être refactoriser tout ça.
Et c’est le moment où on se souvient d’un concept décrit dans un vieil article lu en diagonale à l’époque où on découvrait la magie jQuery : la délégation d’évènement.
Pour le principe, c’est simple : plutôt que d’ajouter un écouteur d’évènement individuel à plusieurs éléments, on ajoute un seul écouteur d’évènement à leurs parent.
Pour les bénéfices, c’est un poil plus abstrait :
- on économise les pointeurs et la mémoire,
- on ne se préoccupe plus des soucis de référence circulaire qui faisaient cracher Internet Explorer,
- on a un code plus facile à lire et à explorer car la gestion des évènements est centralisée,
- la gestion d’évènements ne dépend pas de la présence des éléments dans le DOM au moment de l’ajout de l’écouteur,
- l’ajout ou le retrait d’éléments n’a aucun impact sur la gestion d’évènements.
Avec jQuery c’est pas plus difficile que sans délégation :
var $ma_liste = $("#ma-liste");
$ma_liste.on("click", "a", trucFarfelu);
Pareil en goût-vanille :
var ma_liste = document.querySelector("#ma-liste");
ma_liste.addEventListener("click", trucFarfelu, false);
Un seul écouteur d’évènement.
Un seul gestionnaire d’évènements.
Zero soucis.