Programmer la gestion d'évènements en JavaScript

Il est possible de créer ses propres gestionnaires d'évènement avec des fonctions comme addEventListener et les objets Event, EventListener, DocumentEvent.

Cela remplace les gestionnaires d'évènements prédéfinis sous forme d'attributs HTML ou XML comme onClick, onMouseOver, etc...

Et cela fait partie de la Document Object Model Level 2 Events , une spécification qui décrit une interface aux évènements, autrement dit aux actions de l'utilisateur, pour permettre d'ajouter dynamiquement des gestionnaires d'évènement aux pages HTML ou XML.

On associe une fonction à une action avec addEventListener

Cette méthode associe un gestionnaire d'évènement à tout élément d'une page HTML, de façon dynamique après le chargement de celle-ci.
La syntaxe:

EventTarget.addEventListener(DOMString type, EventListener fonct, boolean)

Exemple:

x.addEventListener('click', fonction, false)

Les paramètres sont dans l'ordre:

  1. Le nom de l'évènement.
  2. Le nom d'une fonction appelée lorsque l'évènement est déclenché.
  3. La valeur true pour le mode capture ou false pour le mode bubbling (voir définitions en base de page).

Toute balise à une interface EventTarget

EventTarget est une interface dont hérite tout élément dans une page HTML. Cela permet de lui associer trois méthodes:

Dans notre exemple, la balise x est de facto, comme toute balise HTML, un EventTarget.

On peut associer la méthode addEventListener à un noeud du document (une balise), au document lui-même (document), une fenêtre (window ou nom de fenêtre), à l'objet XMLHttpRequest.

On peut associer plusieurs fonctions à un évènement et un même élément:

x.addEventListener('click', fonction1, false);
x.addEventListener('click', fonction2, false);

Le nom d'un évènement est une DOMString

L'argument DOMString est une chaîne qui reprend le nom d'un évènement standard. Les évènements reconnus pour la souris sont:

Pour le clavier (DOM 3):

DOM 3 reconnaît d'autres types d'évènements, voir liens plus bas.

La fonction en second paramètre est un EventListener

Le "listener" (écouteur) peut être une fonction définie par l'utilisateur ou un objet implémentant l'interface EventListener.
Pour passer des paramètres à la fonction, on utilise une fonction anonyme.

Le troisième paramètre est une valeur booléenne: mode capture ou non

Elle concerne la capture des évènements du type donné. On mettra false si on n'en ne le veut pas.
Si la valeur est vraie, tous les évènements du type spécifié ici seront passés d'abord à l'EventListener ici avant d'être passés aux autres EventTarget des balises qu'il content. En clair la propagation se fera des balises conteneurs d'abord puis les balises qu'elles contiennent ensuite. Par défaut c'est l'inverse.

Attacher directement l'évènement à un objet

Plutôt qu'utiliser addEventListener on peut attacher directement l'évènement à l'objet représentant la balise dans le DOM.

Par exemple:

var x = documentGetElementById("mabalise");
x.onclick = function(evt) { ... }

ou:

function clickHandler(evt) { ... };
x.onclick = clickHandler;

Cela fonctionne avec tous les navigateurs. Les évènements sont toujours remontés dans ce cas, on ne peut choisir le mode de propagation.

Propagation, comment la stopper

Si un type d'évènement, par exemple onkeydown est attaché à un élément, et que le même évènement est attaché aussi au conteneur de cet élément, quand une touche est pressée dans l'élément, l'évènement est déclenché dans cet élément d'abord, et ensuite dans le conteneur.

Pour éviter la propagation de l'évènement dans les autres balises, on utilise la méthode stopPropagation qui fonctionne avec IE9 et tous autres navigateurs.

function keyhandler(evt)
{
  evt.stopPropagation();
  ... gestion de l'évènement pour x ...
}


x.onkeydown = keyhandler(evt);

Pour IE8 et précédents on utilisera:

evt.cancelBubble = true;  

Certaines combinaisons de touches sont utilisées par le navigateur lui-même, c'est le cas de CTRL-C, CTRL-U, CTRL-S, etc... Ces évènements sont interceptés avant le traitement du DOM du contenu du document ce qui fait que les gestionnaires d'évènements que vous avez créés ne sont jamais utilisés.
Pour récupérer ces évènements dans votre page, vous pouvez désactiver leur traitement par le navigateur avec la méthode preventDefault.

document.onkeydown=function(evt)
{
var code = (evt.keyCode || evt.which);
if(evt.ctrlKey) switch(code) { case 67: // ctrl-c case 85: // ctrl-u
evt.preventDefault(); evt.returnValue = false; .. traitement.. break;
} }

Ces évènements qui sont par défaut gérés au niveau du document ne sont pas transmis aux éléments contenus, ce qui est normal puisque la propagation se fait à partir du contenu vers le contenant (en mode bubbling). Le fait de supprimer leur gestion par défaut permet la reconnaissance par les balises internes dès lors qu'elle à le focus. Si c'est le document qui a le focus, ce qui est le cas quand on utilise le clavier et qu'aucun élément n'est sélectionné, il n'y a pas propagation aux gestionnaires d'évènement des élements du contenu du document.
Ce problème n'existe pas avec la souris car on la déplace ou clique toujours sur un élément contenu qui a alors the focus.

On peut toujours donner le focus à un élément contenu:

document.getElementById("mabalise").focus(); 

Ainsi les évènements associés à cet élément dont l'id est "mabalise" seront traités en premiers avant d'être remontés.

Démonstration: addEventListener remplace les évènements HTML par du code dynamique

A titre d'exemple, on verra comment remplacer l'attribut HTML onClick par addEventListener.

Normalement le gestionnaire d'évènement est associé à un objet, button par exemple:

<input type="button" id="monbouton" value="Envoyer" onClick="mafonction()">

Cela appelle une fonction JavaScript déclarée dans un script:

function mafonction()
{
alert("Cliqué");
}

On supprime le gestionnaire d'évènement onClick:

<input type="button" id="monbouton" value="Envoyer">

On le remplace par un appel à addEventListener:

function addOnClick()
{
var x = document.getElementById("monbouton");
x.addEventListener("click", mafonction, false);
}
window.onload=addOnClick;

On peut remplacer l'action "click" par un des autres types dont la liste est donnée plus haut.

Pour la compatibilité avec Internet Explorer 8 et versions précédentes, on doit utiliser attachEvent:

function mafonction()
{
  alert("Cliqué!");
}

function addOnClick()
{
var x = document.getElementById("monbouton");
if (x.addEventListener)
{
x.addEventListener('click', mafonction, false);
}
else
{
x.attachEvent('onclick', mafonction);
}
}

attachEvent prend comme premier paramètre les gestionnaires d'évènement HTML contrairement à addEventListener.

Utiliser addEventListener semble plus compliqué pour associer une action élémentaire à un élément, comme dans notre exemple, que l'attribut HTML, mais cela ouvre la porte à un ensemble de possibilités bien plus large.
On peut dans une boucle JavaScript associer globalement un gestionnaire d'avènement à un type d'élément dans la page.

Exemple avec onClick

Le code HTML:

<input type="button" id="button1" value="Envoyer" onClick="mafonction()">

Le code JavaScript correspond juste à la fonction définie ci-dessus.

Avec addEventListener

On voit que cela permet de supprimer le gestionnaire d'évènement onClick dans le code HTML.

Le code HTML

<input type="button" id="button2" value="Envoyer">

Le code JavaScript est celui donné ci-dessus.

Créer du code dynamiquement et ajouter un gestionnaire d'évènement

On crée un élément img, on lui assigne une image qui est alors affichée, et un gestionaire d'évènement qui appelle la fonction imageclick quand on clique sur l'image. Noter que la fonction appelée, le listener, peut avoir un argument grâce à l'emploi d'une fonction anonyme dans les paramètre de addListener.

Code source:

  <div id="imgstorage"></div>
<script>
function imageclick(fruit)
{
alert("Ceci est une image de: " + fruit);
}
var s = document.getElementById("imgstorage");
var i = document.createElement("IMG");
i.src="http://www.xul.fr/images/apple.jpg";
s.appendChild(i);
if(i.addEventListener)
{
i.addEventListener('click', function(){imageclick('Pomme');}, false);
}
else
{
i.attachEvent('onclick', function(){imageclick('Pomme');});
}
</script>

Appendice: définitions

Capture et remontée (bubbling)

On parle de capture quand un évènement est traité par la balise conteneur avant d'être transmise à la balise contenue. On parle de remontée (bubbling en anglais) quand l'ordre inverse se produit, ce qui est le standard quand on ne spécifie rien dans le code. Car la propriété addEventListener dispose d'un paramètre pour décider de l'ordre. Si le troisième paramètre vaut true, on est en mode capture, s'il est false en mode bubbling.

Voir aussi

Références

© 2010-2012 Xul.fr