Eviter l’appel immédiat d’une fonction Javascript

Lors que vous souhaitez ajouter un évènement onclick sur un élément DOM depuis votre code javascript, il vous est peut-être déjà arrivé de vouloir factoriser votre traitement et de vouloir appeler une fonction à chaque évènement onClick ou de vouloir y passer des paramètres dynamiquement. Le problème, c’est que javascript a tendance à exécuter immédiatement la fonction au lieu de passer simplement la référence à celle-ci.

Par exemple, lors de l’appel à la fonction test ci-dessous, nous souhaitons ajouter à l’élément monElementDOM préalablement récupéré une fonction maFonction à chaque évènement onclick.

function test(){
      monElementDOM.onclick=maFonction();
}

function maFonction(){
       alert("maFonction");
}

L’inconvénient de cette méthode si vous la testez, c’est qu’au lieu d’appeler la fonction maFonction à chaque évènement onclick sur l’élément monElementDom , Javascript va appeler qu’une seule fois la fonction au moment de l’exécution de la fonction test, et c’est donc le résultat de la fonction maFonction() qui sera appelé à chaque onclick. Notre fonction ne retournant rien, il n’exécutera rien. Mais c’est quoi ce bug bizarre? En fait, il s’agit là du fonctionnement normal de javascript dû à la norme ECMAScript et au fonctionnement de binding de ce langage, mais il est tout à fais possible en adaptant notre script de changer ce comportement.

function test(){
      monElementDOM.onclick= function(){
			return function(){
				return maFonction();
			};
	}();
}

Dans ce cas, la fonction maFonction sera bien exécutée à chaque évènement onclick et non plus à l’initialisation. Dans ce dernier exemple, nous avons encapsuler notre fonction maFonction au sein d’une autre fonction « générique » ne provoquant pas l’exécution immédiate de maFonction. La syntaxe particulière utilisé permet d’appeler notre fonction uniquement lors de l’appel à la méthode apply(). Cette méthode native de javascript est celle qui est appelé automatiquement lors de tout appel à une fonction et qui provoque son exécution. La méthode apply() sera donc appelée implicitement lors des évènements onClick.

Si vous avez fait attention à la syntaxe précédente, vous avez peut-être remarqué les parenthèses vides à la fin, celles-ci vous permette de passer des paramètres à donner lors de l’appel à votre fonction.

function test(){
      monElementDOM.onclick= function(message){
			return function(){
				return maFonction(message);
			};
	}("Bonjour");
}

function maFonction(message){
 alert(message);
}

Ceci vous permet également de passer les variables/paramètres en fonction des valeurs de celle-ci à différent moment du script.

Si par exemple, vous faites ce code ci:

<html>

<script>
function test(){
	for(i=0;i<5;i++){
		document.getElementById('test'+i).onclick=function(){
			return function(){
				return maFonction("message"+i);
			};
		}();
	}
}

function maFonction(message){
       alert(message);
}
</script>

<body onload="test()">
	<div id="test0">Salut à tous</div>
	<div id="test1">Salut à tous</div>
	<div id="test2">Salut à tous</div>
	<div id="test3">Salut à tous</div>
	<div id="test4">Salut à tous</div>
</body>

</html>

Quelque soit l’élément sur lequel vous cliquez, le message sera le même « message5 ». C’est dû au faite que la variable i est interprété au moment de chaque Click. Comme la boucle for s’est terminé, la variable i est resté à sa dernière valeur : 5. (A noter que cet exemple montre très bien que la variable i est public ;-)).

En revanche, si vous souhaitez que la variable i soit interprétée à chaque itération du for, il vous faut passer la variable à la fonction encapsulante. Dans le cas ci-dessous, le message affiché sera alors « message0″, »message1″, »message2″, »message3 », etc…

function test(){
	for(i=0;i<5;i++){
		document.getElementById('test'+i).onclick=function(message){
			return function(){
				return maFonction(message);
			};
		}("message"+i);
	}
}

Ce fonctionnement de binding Javascript est assez particulier à comprendre au début, mais très pratique dans de nombreux cas. Toutefois, certaines librairies couramment utilisées (Prototype,jQuery, etc) proposent leurs propres systèmes d’évènement (compatible tout navigateur), il vous faut dans ce cas rechercher le fonctionnement le plus adapté en fonction de votre librairie utilisée.

[edit 15/12/2010]Ce phénomène étant assez difficile à expliquer, il est très difficile de trouver les bons mots-clés pour faciliter la recherche, n’hésitez pas à m’indiquer en commentaire les mots-clés que vous avez essayés pour trouvez cet article, cela pourra éventuellement aider les autres.[/edit]

A voir aussi:

3 réflexions au sujet de « Eviter l’appel immédiat d’une fonction Javascript »

  1. J’ai du relire 4 fois la phrase : « L’inconvénient de cette méthode si vous la testez, c’est qu’au lieu d’appeler la fonction maFonction à chaque évènement onclick sur l’élément monElementDom , Javascript va appeler qu’une seule fois la fonction au moment de l’exécution de la fonction test, et c’est donc le résultat de la fonction maFonction() qui sera appelé à chaque onclick.  »
    Le mot le plus important étant RESULTAT il faudrait peut-être le mettre en gras ?!
    En tout cas merci pour ce décryptage syntaxique ;o)

  2. ça date, mais si c’est toujours d’actualité :

    « empêcher une fonction js de s’exécuter toute seule » était ma requête google

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.