Évènements, fonctions callback, closure Programmation événementielle est un langage de programmation évènementielle. Au lieu d exécuter des instructions de façon séquentielle, les diverses fonctions sont exécutées selon les évènements qui seront captés, comme par exemple un clic de souris, l appui sur une touche donnée, etc. Capteurs d évènements et fonctions callback Version Historiquement, les éléments peuvent capter un évènement en leur ajoutant un attribut comme onclick par exemple : <a href="somewhere.html" onclick="alert('on a cliqué! )"> Cette solution permet de facilement ajouter des interactions dans une page web, mais possède un inconvénient majeur : la définition des évènements à capter est mélangée au code et ne permet donc pas la séparation du contenu et de la logique des interactions. Capteur d évènement en Pour permettre de séparer les interactions et le code, on peut ajouter un capteur d évènement sur un élément de façon programmatique. Deux possibilités pour faire cela : monelement.onclick = faireaction; // attention sans parenthèses!! monelement.addeventlistener( click, faireaction, false); // version DOM Events La référence à l action à exécuter (ici faireaction) est ce qu on appelle un callback : il s agit juste de définir le nom de l action à exécuter, ce qui est différent d un appel à une fonction (qui serait alors fait avec des parenthèses : faireaction(); ) Le DOM permet aussi d enlever un capteur d évènement : monelement.removeeventlistener( click, faireaction, false); Fonctions callback Une fonction callback est une fonction passée en paramètre. C est typiquement ce qui se passe avec l instruction monelement.addeventlistener( click, faireaction, false). La fonction faireaction sera exécutée lorsque l évènement click est capté. La difficulté des fonctions callback par rapport à la programmation classique est que l on ne peut pas choisir les arguments de la fonction. En, toute fonction callback exécutée suite à la captation d un évènement reçoit en argument l objet de type Event.
L objet Event en L objet Event de contient des informations sur l évènement et des méthodes pour agir (par exemple pour l arrêter). Propriétés : target : c est l élément sur lequel se trouve la souris lorsque l évènement est capté currenttarget : c est l élément qui a capté l événement (peut être différent de target) type : le type de l élément (click, focus, mouseover, etc.) Méthodes : stoppropagation : stoppe la propagation de l évènement dans l arbre DOM (cf. plus bas) preventdefault : empêche l élément qui a capté l évènement de déclencher l action implicite. Par exemple si un élément <a> capte un clic, alors le lien est activé, sauf si on l y empêche avec la méthode preventdefault(). Exemple : <a id= test href= http://google.com >bla bla </div> document.getelementbyid( test ).addeventlistener( click, stoppelien); function stoppelien(ev) { console.log(ev.type); ev.preventdefault(); // empêche le lien d être activé lors du clic Propagation des évènements Après la capture d un évènement, celui-ci remonte à la surface de l arbre DOM (phase de bubbling). Si plusieurs éléments dans la branche du DOM doivent capter le même type d évènement, alors l élément le plus profond dans l arbre capte l élément en premier puis l évènement est alors capté par ses ancêtres jusqu à la racine du document. Exemple : <div id= elt1 > <p id= elt2 >un texte <strong id= elt3 >en gras</strong></p> </div> document.getelementbyid('elt1').addeventlistener('click', affiche1); document.getelementbyid('elt2').addeventlistener('click', affiche2); document.getelementbyid('elt3').addeventlistener('click', affiche3); function affiche1(ev) { console.log("élément elt1 a capté le clic");
function affiche2(ev) { console.log("élément elt2 a capté le clic"); function affiche3(ev) { console.log("élément elt3 a capté le clic"); function affiche(ev) { console.log(ev.currenttarget); Exécution lorsque l on clique sur le texte en gras, la console affiche alors : Élément elt3 a capté le clic Élément elt2 a capté le clic <p id="elt2"> Élément elt1 a capté le clic <div id="elt1"> Fonctions anonymes permet l utilisation de fonction anonymes, qui permettent parfois d écrire un code plus concis. Exemple monelt.onclick = function(ev) { console.log( evenement capté ); Fonction anonyme et évènement load Toute page web possède l évènement load sur l objet window qui est déclenché lorsque la page est complètement chargée, c est à dire que le navigateur a reçu tout le code de la page. On utilise cet évènement pour initialiser les interactions de la page web : Objet this window.onload = function() { // mettre les capteurs d évènements document.getelementbyid( test ).addeventlistener( click, faireaction, false); // En programmation orientée objet, le mot-clé this désigne en général l objet dans lequel le code se trouve. C est par exemple le cas avec les langages Java ou PHP entre autres. Dans la cadre de son utilisation en, this peut lui aussi désigner l objet dans le
quel on travaille, mais lorsque est utilisé en programmation évènementielle, l objet this est alors l objet qui a capté l évènement déclencheur de l action en cours. Exemple <div id= elt >une div qui capte en clic</div> document.getelementbyid('elt').addeventlistener('click', affiche); function affiche(ev) { console.log("élément elt a capté le clic"); console.log(ev.currenttarget); // affiche <div id= elt > en console car c est l élément qui a capté le clic console.log(this); Notion de closure En, une fonction peut être contenue dans une fonction parente, par exemple: var parent = function(jour, mois) { var afficher = function() { var ladate = new Date(annee, mois, jour); console.log('the date entered is ' + ladate.todatestring()); var annee = 2015; afficher(); parent(12, 2); // affiche : The date entered is Thu Mar 12 2015 afficher(); // ReferenceError: afficher is not defined La fonction afficher() est seulement définie à l intérieur de la fonction parent(). Mais la notion de closure est le fait que la sous-fonction a accès aux variables de la fonction englobante. Elle a accès aux variables locales dans sa portée, puis aux variables de la fonction englobante (et ses paramètres), et enfin aux variables globales. Les closures sont beaucoup utilisées en et il est important de connaitre leurs principes de base. Attention cependant, la closure stocke une référence vers les variables de la fonction englobante, et non leur valeur. Cela pose alors parfois des problèmes si la valeur d une variable change avant l exécution de la closure (ou pendant son exécution). L exemple typique est avec l utilisation de boucles. Prenons l exemple fourni pat SitePoint (voir lien plus bas) :
<html lang="en"> <head> <title>closures</title> <meta charset="utf-8" /> <script> window.addeventlistener("load", function() { for (var i = 1; i < 4; i++) { var button = document.getelementbyid("button" + i); button.addeventlistener("click", function() { console.log( Clicked button " + i); ); ); </script> </head> <body> <input type="button" id="button1" value="one" /> <input type="button" id="button2" value="two" /> <input type="button" id="button3" value="three" /> </body> </html> Quel que soit le bouton cliqué, cela affichera Clicked button 4 puisque la boucle est forcément finie avant qu un bouton ne soit cliqué, et donc i vaut 4. La solution est alors de découpler la closure de la boucle. La solution est expliquée dans le tutoriel de SitePoint : http://www.sitepoint.com/javascript-closures-demystified/ Autre tutoriel sur les closures : http://javascriptissexy.com/understand-javascript-closures-with-ease/ Conclusion La gestion des évènements avec nécessite de l entrainement pour s extraire de la logique de la programmation séquentielle. La séparation du code et de la logique des interactions permet de créer des bibliothèques de fonctionnalités en qui seront réutilisées dans diverses pages web. Les closures sont difficiles à maitriser lorsque l on débute en. Nous serons amenés à les utiliser lors de divers exercices manipulant l arbre DOM et utilisant la gestion d évènements.