Nouvelles Chroniques d'Amethyste

Penser au Sens, pas au Verbe

javascript: les événements

Poster un commentaire

JavaScript offre un support pour les événements que nous allons étudier ici. Inutile de dire que dans la grande tradition JavaScriptienne rien n’est simple!

Mais s’il y a des tas de choses que l’on peut occulter en JavaScript, les événements n’en font pas partie. Sans eux, impossible de faire des pages un peu intelligentes. Les événements sont responsables des fonctions d’interactivités offertes par les pages Web.
Il existe toutefois aussi quelques événements dont le déclenchement ne doit rien à l’interaction avec l’utilisateur. Citons par exemple load.

Lorsqu’un événement se déclenche il faut que quelqu’un réagisse pour que cela soit utile. Le quelqu’un est une fonction appelée gestionnaire d’événements.

A part son rôle, un gestionnaire d’événement est une fonction tout à fait quelconque.

Notez bien la différence: L’événement est déclenché en réaction à une interaction, une étape du cycle de vie de la page… Le gestionnaire d’événement est la méthode qui est appelée lorsque l’événement est déclenché.
Je suis toujours surpris en interview du nombre de développeurs qui ne connaissent pas la différence.

Un événement peut déclencher plusieurs gestionnaires, par contre un gestionnaire ne peut réagir qu’à un seul événement (encore que techniquement… mais c’est pas terrible).

Avant de lire la suite, je conseille volontiers de lire l’article sur this, des fois que…

Le modèle historique

C’est celui introduit par Netscape 2.0, le premier navigateur ayant introduit JavaScript. Netscape a ajouté à quelques éléments HTML, comme les images, les boutons… , une série d’événements auxquels on peut attacher (ou abonner) des gestionnaires d’événements.

Par exemple un lien réagit à des événements tels click ou onMouseOver. Un exemple typique de rattachement est celui-ci:

<a href= » » onClick= »NomFonction« >

NomFonction est le nom du gestionnaire d’événement. On ne met surtout pas les parenthèses!

Ce modèle d’événement est limité, mais efficace et puisqu’il a été le premier il est de fait bien respecté par tous les navigateurs.

Ceci étant posé, examinons les différentes approches pour enregistrer un événement.

La méthode inline

On en a vu une qui admet des variantes comme la méthode inline. Puisque l’on a commencé à mettre du JavaScript, autant continuer…

<a href= » » onClick= »gestionnaire();return false; »>

L’idée c’est donc de mélanger le JavaScript et le code HTML. C’est souvent la seule que maîtrise les développeurs et malheureusement pour eux:

C’est surtout celle qu’il est bon d’éviter afin de ne pas mélanger HTML et code.

La méthode traditionnelle

Introduite par Netscape 3. L’idée est de récupérer une instance vers l’objet qui nous intéresse (appel au DOM, JQuery peu importe) et d’écrire par exemple pour l’événement onClick:

element.onclick = gestionnaire;

Notez bien la syntaxe, le gestionnaire n’est pas accompagné de parenthèses.
Si par malheur vous placiez les parenthèses, la fonction serait exécutée et c’est le résultat qui serait versé dans la propriété. Pas certain que ça marche bien.

Donc chaque objet expose une propriété, celle-ci se voit affectée le nom d’une fonction. La propriété est entièrement en minuscule.

Pour supprimer le gestionnaire:

element.onclick = null;

Cette fois pas de pollution du HTML par du JavaScript ce qui est un plus. De plus au vu de son ancienneté, tous les navigateurs supportent cette méthode. C’est donc cool.

Pas tout à fait. Rien ne dit que la propriété soit la même pour chaque navigateur. On ne peut donc pas se passer de la table de compatibilité.

http://www.quirksmode.org/dom/events/index.html

Et

http://www.quirksmode.org/js/events_events.html

Un autre problème peut se poser. Le modèle traditionnel ne permet d’enregistrer qu’un seul gestionnaire. Mais il existe un contournement avec une fonction anonyme:

element.onclick = function () {log(); process(); }

La méthode du W3C

Le cœur de la méthode se trouve la fonction addEventListener().

La méthode attend 3 arguments:

  1. le type de l’événement
  2. le gestionnaire
  3. un booléen

Par exemple:

element.addEventListener(‘click’, gestionnaire, false);

Note: Pas de parenthèses après le nom du gestionnaire vous savez pourquoi.

Cette fois on peut ajouter autant de gestionnaires que l’on souhaite, la méthode s’appelle en effet add (ajouter) et c’est pas par hasard…

element.addEventListener(‘click’, log, false);

Mais attention, le W3C ne dit rien quand à l’ordre d’exécution des gestionnaires. Ne faites donc pas d’hypothèses.

Pour supprimer un gestionnaire:

element.removeEventListener(‘click’, log, false);

Il n’y a pas de moyens évident de faire cela avec le modèle traditionnel.

Je n’ai pas encore parlé du dernier paramètre, le booléen. Il est lié à l’ordre d’exécution des événements. La version longue est ici:

http://www.quirksmode.org/js/events_order.html

La version courte: la plupart du temps on mettra false.

Notez un dernier point important. Le gestionnaire est copié, cela signifie que this se réfèrera à l’objet où il a été ajouté.

La méthode Microsoft

Ce modèle ressemble en partie à celui du W3C.
Il y a d’abord une différence de syntaxe, on ajoute pas un gestionnaire d’événement à un événement, on attache un gestionnaire d’événements (ou plusieurs):

element.attachEvent(« onclick »,gestionnaire);

On peut le supprimer:

element.detachEvent(« onclick »,gestionnaire);

Ce modèle souffre de plusieurs défauts et en particulier:

  • Le gestionnaire d’événement est référencé, pas copié. Le résultat est que this se réfère toujours à Windows, jamais l’objet et est donc sans intérêt.
  • Par ailleurs ce modèle ne prend en charge QUE le mode bubbling (on va voir ça plus loin).

Donc du très mauvais travail qu’il n’est pas plus mal d’éviter.

De toute façon ces deux modèles ne son que rarement supportés, le mieux est de les considérer comme dépréciés et en rester à des pratiques plus traditionnelles.

Et JQuery?

D’un point de vue pratique aucunes de ces méthodes n’est réellement utilisées ou conseillées. La façon de loin la meilleure d’attacher un gestionnaire d’événement est de laisser JQuery se débrouiller.

Attention: JQuery n’apporte rien de nouveau, rien d’autre qu’une syntaxe plus simple. Et c’est déjà pas mal.

Les moyens à votre disposition sont résumés ici:

https://api.jquery.com/category/events/event-handler-attachment/

Si vous regardez bien il y a pas moins de trois méthodes pour attacher un gestionnaire d’événement à un élément: bind(), live() et on(). Voire plus si on considère delegate() et one().

On ne peux pas parler de simplification me direz vous!

En fait l’usage tend à privilégier on() et one(), il est bien probable que les deux autres finissent par être dépréciées d’autant plus qu’en interne elles sont mappées à on().

https://github.com/jquery/jquery/blob/1.7/src/event.js#L965

La syntaxe extraite de la doc parle d’elle même:

$("p").on("click", function() {
  alert( $(this).text());
});

Ordre d’exécution des événements

On a deux éléments imbriqués:

01-03-2014 15-53-22

Si je clique au dessus de Element2 les deux éléments reçoivent l’événement, mais lequel en premier?

  • Selon Netscape: Element1 reçoit l’événement en premier. Il est ensuite passé à Element 2 et ainsi de suite jusqu’à atteindre l’élément cible
    On parle de capture d’événement.
  • Selon Microsoft: Element2 prend l’événement en premier.
    Il redescend ensuite la hiérarchie des éléments jusqu’à Element 1
    On parle de bubbling (percolation des événements)

IE ne supporte qu’un modèle: bubbling. Certains navigateurs supportent les deux, d’autres aucuns (assez ancien tout de même).

Ce n’est pas tout, le W3C a décidé de se mêler à la discussion et a produit son propre modèle:

  • L’événement est d’abord capturé (modèle Netscape) jusqu’à atteindre l’élément cible
  • Il est ensuite percolé (modèle Microsoft)

Résumons:

01-03-2014 18-55-21

Il est possible de décider entre le modèle Netscape et le modèle Microsoft au moment d’enregistrer l’événement:

element1.addEventListener(‘click’,gestionnaire,true);

Le troisième argument est un booléen. La valeur true active l’événement pour la phase Netscape (capture), tandis que false l’active pour la phase Microsoft (bubbling).
Examinons le code suivant:

  • element1.addEventListener('click',gestionnaire1,true);
    element2.addEventListener('click',gestionnaire2,true);
    

    Le gestionnaire est activé pour la phase capture dans Element1 et pour la phase bubbling dans Element2.

  • Supposons qu’un click soit déclenché sur Element2. Il démarre dans sa phase capture.
  • L’événement regarde s’il existe un ancêtre de Element2 qui a un gestionnaire actif pour la phase capture
  • Il trouve Element1
  • gestionnaire1() est déclenché
  • L’événement se déplace alors ver Element2
  • Aucun gestionnaire actif pour la phase capture n’est trouvé
  • La phase bubbling démarre à partir de Element2
  • Element2 a un gestionnaire actif pour la phase bubbling
  • gestionnaire2() est déclenché
  • l’événement regarde si un ancêtre de Element2 a un gestionnaire actif pour la phase bubbling
  • il n’y en a pas, tout s’arrête là

Si vous souhaitez en savoir plus, lisez cet excellent article:

http://www.quirksmode.org/js/events_order.html

Obtenir la cible d’un événement

La cible d’un événement est renvoyée par target dans le modèle Netscape et srcElement par le modèle Microsoft. D’où le code générique:

if (e.target)
	targ = e.target;
else if
		(e.srcElement) targ = e.srcElement;

if (targ.nodeType == 3) // bug Safari
	{
		targ = targ.parentNode;
	}

L’action par défaut

L’action par défaut d’un événement est celle que l’on s’attend à le voir faire. Par exemple un click sur un lien nous amène vers une autre page.

Depuis Netscape 2 nous avons vu qu’il est possible de rattacher du code à cet événement, par exemple un code qui valide la possibilité de lancer l’action par défaut.

Que devient alors la notion d’action par défaut? La règle est la suivante:

  • Le gestionnaire d’événement est exécuté en premier
  • l’action par défaut est exécutée ensuite

Bien sûr on a parfois besoin de bloquer l’action par défaut.

Pour cela le gestionnaire d’événements doit renvoyer un booléen. La valeur false bloque l’événement par défaut comme sur cet exemple:

<a href= » » onClick= »gestionnaire();return false; »>

Cette technique a été standardisée par Netscape et tous les navigateurs la suive. Il y a toutefois une exception très curieuse: onMouseOver attend non pas false, mais true.

Retourner false est la base, on est certain que ça marche. Mais il existe des alternatives:

  • Le W3C ajoute la méthode preventDefault().
  • Microsoft a complété l’objet Event avec returnValue qui prend false pour bloquer l’événement par défaut

On pourrait donc écrire le code générique suivant:

// event est l'objet Event
if (event.preventDefault) {
event.preventDefault(); // W3C
}
event.returnValue = false; // MS

Et ça marche avec tous les navigateurs qui supportent au moins une de ces méthodes.

Pour tester ce code:

<a onclick="gestionnaire(event);" href="http://www.bing.com" target="_blank">Bing (validation)</a>

<a href="http://www.bing.com" target="_blank">Bing (sans validation)</a>

<script type="text/javascript">// <![CDATA[
function gestionnaire (event) {
// event est l'objet Event

// ici une logique qui décide ou non d'appliquer les lignes qui suivent

 if (event.preventDefault) {
     event.preventDefault(); // W3C
 }
 event.returnValue = false; // MS
}
// ]]></script>

Peut t’on stopper toutes les actions par défaut?

La réponse est non. On ne peut pas bloquer unload par exemple.

Quels sont les événements supportés?

Ils sont nombreux et chaque navigateur à ses particularités. Voici une table de compatibilité des événements:

http://www.quirksmode.org/dom/events/index.html

On pourrait consacrer de nombreuses pages à cette question, le plus simple est de lire cet article qui résume l’essentiel:

http://www.quirksmode.org/js/events_events.html

Bibliographie

http://www.quirksmode.org/js/events_order.html

http://www.misfu.com/static/Javascript/this.html

http://www.quirksmode.org/js/introevents.html

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s