Mon cahier d’exercices

Utilisez les événements !

"Auteur : Hagnoul Daniel"

Texte : v1.1.0 2010-02-12
Texte : v0.8.6 draft 2009-10-17

Introduction

Je ne pense pas que l’on puisse utiliser jQuery sans maîtriser les événements fondamentaux, ils ne sont donc pas l'objet de notre propos. De même pour les événements spéciaux (exemple : mouseenter, mouseleave), voir
https://brandonaaron.net/blog/2009/03/26/special-events et
https://brandonaaron.net/blog/2009/06/4/jquery-edge-new-special-event-hooks.

Les navigateurs internet traitent de manière anarchique les événements JavaScript, mais jQuery s'occupe en interne des particularités de chacun et offre en externe une interface unique. Il possède un système événementiel élaboré, assez facile à appréhender, mais méconnu et sous-exploité si j'en juge par les questions posées sur le forum jQuery.

Une page internet doit toujours être asynchrone. C'est l’utilisateur final, et lui seul, qui décide ce qui doit se passer à l'instant suivant, il doit toujours "avoir la main." C’est la raison pour laquelle toutes les fonctions "callback" sont asynchrones en jQuery.

JavaScript n’est pas un vrai langage-objet, mais votre code doit utiliser les objets et il doit déclencher et gérer les changements d'état d'objets à l'aide d'événements.

Utiliser les objets sans les événements c'est comme si vous vous attachiez un boulet à chaque pied avant de courir.

Définitions personnelles :

  1. une « fonction classe » désigne une fonction et son prototype. Une « fonction classe » permet la création d’un objet en se servant de l’opérateur new.
  2. une « fonction méthode » désigne une fonction incluse dans le prototype de la fonction classe ou dans un Objet JSON.

Un processus en quatre étapes

  1. On attend un événement :
    $(this).bind("allumeEvent", allumeEventHandler);
  2. On crée un nouvel événement :
    var monEvent = new $.Event("allumeEvent");
  3. On déclenche l’événement :
    $(this).trigger(monEvent);
  4. On consomme l’événement dans la fonction
    allumeEventHandler(event){…};

Nota Bene

Un événement non intercepté est perdu.

L’objet $.Event()

Avant de voir quelques exemples d'utilisation, nous allons parcourir très succinctement les points clés de la documentation du système d'événement jQuery ce qui vous facilitera peut-être l'indispensable lecture de la documentation originale (https://api.jquery.com/bind/ et https://api.jquery.com/category/events/event-object/).

Je vous conseille de considérer les propriétés de l'objet $.Event() comme étant en lecture seule, car jQuery les utilise et les modifie à sa guise.

Propriété Description
event.currentTarget L'élément DOM concerné par l'événement lors de la phase de bouillonnement (bubbling).
event.data Contiens les données facultatives passées à bind lorsque le gestionnaire d'exécution actuel était lié.
event.pageX La position de la souris par rapport au bord gauche du document.
event.pageY La position de la souris par rapport au bord supérieur du document.
event.relatedTarget Les autres éléments DOM impliqués dans l'événement, le cas échéant.
event.result La dernière valeur retournée par le gestionnaire d'événement qui a été déclenché par cet événement, à moins que cette valeur n'ait pas été définie.
event.target L'élément DOM qui a initié l'événement.
event.timeStamp Cet attribut retourne le nombre de millisecondes depuis le 1er Janvier 1970, lorsque l'événement est déclenché.
event.type Décrit la nature de l'événement.
event.which Pour les événements clavier ou souris, cet attribut indique la touche ou le bouton qui a été enfoncé.
Fonction Description
event.preventDefault() Si cette méthode est appelée, l'action par défaut de l'événement ne sera pas déclenchée.
event.isDefaultPrevented() Retourne « true » si event.preventDefault() a été demandé sur cet événement.
event.stopPropagation() Cette méthode empêche le « bouillonnement » de l'événement dans l'arbre DOM, ce qui empêche tout gestionnaire parent d'être informé de l'événement.
event.isPropagationStopped() Retourne « true » si event.stopPropagation() a été demandé sur cet événement.
event.stopImmediatePropagation() Empêche les autres gestionnaires d'événements d'être appelés.
event.isImmediatePropagationStopped() Retourne « true » si event.stopImmediatePropagation() a été demandé sur cet événement.

Après avoir créé un nouvel événement, vous pouvez lui ajouter vos données :
var monEvent = new $.Event("allumeEvent");
monEvent.dvjh = {...};

Espace de noms

Vos événements doivent existés dans un espace de noms.

Exemple :
var monEvent = new $.Event("allumeEvent.monEspaceDeNom");

L'espace de nom est très utile :

Nota Bene

L'événement $.Event("appendAffiche.type2")
ne sera pas intercepté par
$("#affiche").bind("appendAffiche.type1", appendAfficheEventHandler).

Mais l'événement $.Event("appendAffiche")
sera intercepté partout, ce qui peut avoir son utilité pour déclencher l'événement appendAffiche dans tous les espaces de nom (exemple : type1, type2, etc.).

Exemple n° 1

Exemple n° 1

/*
 * On déclare attendre un événement en utilisant bind()
 * On attend un événement du type appendAffiche dans l'espace de nom type1
 * On consommera l'événement dans la fonction appendAfficheEventHandler(event)
*/
$("#affiche").bind("appendAffiche.type1", {usage: "fréquent"}, appendAfficheEventHandler);

/*
 * On construit l'événement du type appendAffiche dans l'espace de nom type1.
*/
var appendEvent = new $.Event("appendAffiche.type1");			
appendEvent.dvjh = {
	utilisateur: "Daniel Hagnoul",
	css: {
		color: "red",
		fontSize: "2em"
	},
	data: {
		un: 1,
		deux: 2,
		trois: 3
	},
	dimArray: new Array(24, 36, 48, 52, 64)
};

/*
 * On déclenche l'événement en utilisant trigger()
*/
$("#btnAffiche").click(function(){
	$("#affiche").trigger(appendEvent);
});

/*
 * On consomme l'événement
*/
function appendAfficheEventHandler(event){
	...
	
	return false;
}

Exemple n° 2

Exemple n° 2

Comme d'habitude, jQuery possède une écriture alternative que certains appellent même simplifiée. Je vous conseille de l'utiliser uniquement lorsque vous n'avez pas ou très peu de données à transmettre avec l'événement.

/*
 * On déclare attendre un événement et on consomme l'événement en utilisant
 * une fonction anonyme comme handler.
*/
$("#affiche").bind("appendAffiche.type2", {usage: "peu fréquent"}, function(event, utilisateur, dim){
	...
	
	return false;
});

/*
 * On construit et on déclenche l'événement en utilisant trigger()
*/
$("#btnAffiche").click(function(){
	$("#affiche").trigger("appendAffiche.type2", ["Daniel Hagnoul", "102"]);
});

Une transaction AJAX

Introduction

jQuery déconseille l’usage de : $.ajax(options) et recommande l'usage des transactions sécurisées :

Mais si j’en crois les questions posées sur le forum jQuery, c’est pourtant l’option déconseillée qui est la plus utilisée. On peut penser qu’il en est ainsi, car beaucoup non pas apprit comment gérer l’échec de la connexion avec les transactions plus sécurisées.

Une division modifiable

Voici l’exemple d’une transaction $.getJSON(). La gestion d’erreur est toujours identique, quel que soit le type de la transaction.

On crée un objet JSON – DivModifiable – qui contient toutes les propriétés et fonctions méthode nécessaires. Toute la « logique » de création et de traitement de la division est contenue dans DivModifiable.

Exemple n° 3

Une queue d’événements

Je vous entends : « L’exemple d’une transaction AJAX était intéressant, mais dans un programme plus complexe le nombre d’événements générés dans un court intervalle de temps peut-être important, comment va-t-on faire pour déclencher, intercepter et traiter ces événements concomitants ? »

« On peut avoir besoin de lancer et d’intercepter plusieurs fois le même événement. Comment fait-on ? »

L’exemple n° 4 est assez simpliste, mais il répond aux questions ci-dessus. Il est composé de quatre fonctions classe : Pair, Impair, Table et Total.

Exemple n° 4

Les fonctions classe Pair et Impair stockent un nombre et ont une fonction méthode add(obj). Cette fonction méthode ajoute un au nombre stocké et envoie un événement à obj si la condition est remplie (pour la fonction classe Pair il faut que le nombre stocké soit pair et pour la fonction classe Impair il doit être impair).

La fonction classe Table est chargée de l’interface utilisateur. L'objet Table présente les résultats sous forme de tableau triable, pour cela il a besoin du plugin tablesorter et de la feuille de style trierTable. Ces éléments doivent être incorporés au niveau du programme appelant.

L’objet Table stocke au fur et à mesure du déroulement du programme toutes les données envoyées par l’objet qui le contrôle (Total), il construit et affiche la table lorsque sa fonction méthode print() est sollicitée.

La fonction classe Total est le cœur du programme. L’utilisateur démarre le programme en créant un objTotal(millisecondes, divID). Si le paramètre millisecondes n’existe pas il est fixé à 1000 soit 1s, il s’agit du temps d’exécution du programme. Le paramètre divID reçoit le nom attribué à l’id de la division qui affichera la table.

L’objet Total crée les objets Pair, Impair et Table. Il reçoit les données envoyées par Pair et Impair. Après traitement (il stocke le total de tous les nombres reçus), il envoie le résultat à l’objet Table. Lorsque le temps est écoulé, l’objet Total termine le programme en demandant l’affichage de la table.

Les objets communiquent uniquement par l’intermédiaire d’événements.

Live

D’aucuns se seront étonnés de ne pas voir d’exemple avec « live. »
Il y a plusieurs raisons :

$.proxy()

Définition

Il sert à lier un gestionnaire d'événement à un élément dont le contexte est dirigé vers un objet différent.

$.proxy(fonction, contexte);

On écrira donc : $(this).bind(eventName, $.proxy(obj.eventHandler, obj));
au lieu de : $(this).bind(eventName, eventHandler);

Car si eventHandler est une fonction d’un objet distinct de l’objet en cours dans la fonction bind() ou live() vous obtenez une erreur du genre : « this.nomFonction is not a function. »

Nota Bene

$.proxy() est l’équivalent de : return obj.maFonction.apply(obj, [e]);

On ne doit pas oublier cette écriture, car elle permet de transmettre d’autres données : obj.maFonction.apply(obj, [e, param1, param2]);

Exemple $.proxy()

function SlideShow(base){
	var self = this;
	
	$('.leftControl', base).bind('click', $.proxy(self.onLeftControlClick, self));
	//$('.rightControl', base).bind('click', $.proxy(self.onRightControlClick, self));
	
	/*
	 * Sans $.proxy() on obtient l'erreur "this.manageControls is not a function"
	 * car "onRightControlClick this : [object HTMLButtonElement]"
	 */
	//$('.rightControl', base).bind('click', self.onRightControlClick);
	
	/*
	 * ancienne méthode apply ou call
	 * il est possible d'ajouter des arguments
	 */
	$('.rightControl', base).bind('click', function(e){
		var n = 10;
		
		self.onRightControlClick.apply(self, [e, n]);
		//self.onRightControlClick.call(self, e, n);
	});
	
	this.manageControls();
}
 
SlideShow.prototype = {
	manageControls: function(){
		alert("manageControls OK");
	},
	animate: function(){
		alert("animate OK");
	},
	onLeftControlClick: function(){
		alert("onLeftControlClick this : " + this);
		
		this.manageControls();
		this.animate();
	},
	onRightControlClick: function(){
		var tab = $.makeArray(arguments);
		
		alert("onRightControlClick this : " + this + "\nArguments = " + tab);
		
		this.manageControls();
		this.animate();
	}
};

$(function(){
	new SlideShow("#affiche");
});

À bientôt !

 

Veuillez poser vos questions
techniques sur les forums DVP,
s'il vous plait.

Avertissement solennel

Autodidacte en informatique, l’auteur a tous les défauts de sa qualité. Si vous vous inspirez, copiez ou pire utilisez le contenu de cette page, vous êtes téméraire, inconscient du danger !

Reconnaissant avoir été dûment informé, vous déchargez l’auteur et à fortiori l’hébergeur du site de toute responsabilité dans les graves dégâts que vous causerez sûrement !

Vous n'aurez jamais fini d'apprendre la programmation, aussi apprenez à apprendre en vous amusant.

Tutoriel du 03/09/2009

Je vous signale que les trois premières fondations sont reprises dans un tutoriel : Outils pour construire un code jQuery évolutif.

Creative Commons License Attribution-Share Alike 2.0 Belgium Except where otherwise noted, content on this site is licensed under a Creative Commons License : Attribution-Share Alike 2.0 Belgium

Mention obligatoire : "Auteur : Hagnoul Daniel (https://www.developpez.net/forums/u285162/danielhagnoul/)"