Cours de JavaScript - Gestion des événements et DOM

version 1.5.1, dernière mise à jour le 9 novembre 2016.

   

Table des matières (TdM)

  1. I. Introduction
  1. II. Le flot d'événements
    1. Propagation d'événements
    2. Event bubbling (remontée d'événements)
    3. Event capture (capture d'événement)
      1. Le standard
    4. Exemple
  1. III. Comment appeler un événement?
    1. Par un attribut HTML
      1. Rappels
      2. Compatibilité inter-navigateurs
      3. Les attributs possibles
      4. Limitations
    2. Par l'utilisation d'une propriété d'un élément
      1. Exercice : Utilisation des propriétés des éléments
    3. Par des « event listeners »
      1. Principe
      2. Exemple
      3. Le cas Internet Explorer
      4. Exercice : Utilisation simple d'un gestionnaire d'événement
  1. IV. L'objet event
    1. Propriétés et méthodes générales
      1. Le standard
      2. Internet Explorer
    2. Événements souris
      1. Propriétés
      2. Remarques
      3. Compatibilité inter-navigateurs
      4. Exercice : Manipulation des gestionnaires d'événements
      5. Exercice : Manipulation des gestionnaires d'événements (suite)
    3. Événements clavier
      1. Introduction
      2. Internet Explorer
      3. Netscape et FireFox
    4. Événements divers
      1. Événements liés à la manipulation de la fenêtre
      2. Évenements liés au drag&drop
      3. Événements liés au copier/coller
      4. Événements liés aux animations CSS
      5. Événements liés à la connexion Internet

Retour au menu

Contenu du cours

I. Introduction

Dans le cadre d'une stricte séparation entre le fond, la forme et les comportements dynamiques d'une page, le fond est pris en charge par le code HTML, la forme par la feuille de style CSS et les comportements dynamiques par le JavaScript.

L'utilisateur interagit avec une page par l'entremise d'événements.

La plupart des événements sont reliés à l'interface utilisateur du navigateur, tels les déplacements de souris, les actions sur des boutons ou des touches du clavier, les réponses à des formulaires... Ces événements sont en général reliés à un élément particulier dans la page. D'autres sont des événements activés en réponse à une action spécifique du navigateur (par exemple le chargement d'un document, voire d'une image seule).

Quelques objets ont des actions définies par défaut en réponse à certains événements. Par exemple, un clic sur un élément a active le lien hypertexte, et force le navigateur à charger l'URL indiquée dans l'attribut href.

En fait, tous les événements suivent le même modèle. Le DOM fournit des méthodes pour capturer les événements et lancer une action quelconque en réponse. Il définit également un objet event, dont les propriétés permettent d'accéder à des informations spécifiques à l'événement (par exemple, quelle touche du clavier a été activée...).

II. Le flot d'événements

1. Propagation d'événements

Revenons un instant sur ce que recouvre la notion de Modèle Objet de Document. Cela signifie que tout nœud du document (à l'exception de l'élément racine bien entendu) possède une collection d'ancêtres dans l'arborescence. Par conséquent, quand par exemple on clique sur un élément, cela signifie que l'on clique aussi sur chacun des éléments qui le contiennent. Considérons par exemple le code HTML ci-dessous :

<div>
  <p>Un peu de texte dans un paragraphe.</p>
  <p>Un autre texte dans un deuxième paragraphe.</p>
  <p>Encore du texte dans un paragraphe, avec <a href="index.html">un lien</a>.</p>
</div>

Si on clique sur le lien dans le troisième paragraphe, sera généré un événement click. Mais on aura également cliqué sur le paragraphe contenant le lien, ainsi que sur l'élément div contenant le paragraphe, aussi bien que sur l'élément body englobant le tout...

N'importe lequel des éléments de cette chaîne peut avoir un gestionnaire d'événement associé au clic de souris, quel que soit l'élément duquel l'événement est parti.

Selon les standards, il existe deux manières pour un événement de « naviguer » de haut en bas de la chaîne d'éléments : il s'agit de l'event bubbling et de l'event capture.

>Retour à la TdM

2. Event bubbling (remontée d'événements)

Le comportement par défaut des éléments est appelée l'event bubbling. Il recouvre ce concept de remontée des événements le long de l'arborescence du fichier HTML, tout comme une bulle monte dans un liquide. Un gestionnaire d'événement invoqué au cours de cette remontée de l'événement dans l'arborescence du DOM peut stopper cette remontée ; mais si rien n'est fait, l'événement poursuit sa propagation jusqu'à la racine. Tout élément rencontré dans l'arborescence au cours de cette phase peut avoir un gestionnaire d'événement.

Cette phase est appelée bubble phase dans le DOM. Tous les événements ne remontent pas de la sorte ; par exemple focus et blur ne remontent jamais. De même, certains événements ne peuvent être annulés à un moment quelconque de leur remontée. On peut le savoir grâce à l'objet event et à ses propriétés, ainsi que nous le verrons.

>Retour à la TdM

3. Event capture (capture d'événement)

a. Le standard

Le pendant de la phase de bubbling est la phase de capture. En effet, par exemple dans le cas d'un clic, le logiciel commence par détecter qu'un clic a eu lieu. Il lui faut ensuite explorer l'arbre pour identifier l'élément sur lequel l'utilisateur a cliqué. Il est possible de lancer un gestionnaire d'événement quand le navigateur inspecte l'arbre en descendant dans l'arborescence. Ainsi, la phase de capture a lieu en premier ; l'événement descend le long de l'arborescence du document depuis la racine jusqu'à l'élément, avant de remonter.

Lors de cette phase, un élément parent est affecté par un événement avant que ce dernier ne se propage jusqu'à l'élément sur lequel l'événement a effectivement eu lieu. Cela permet de gérer des priorités, et permet d'intercepter un événement pour un élément, quel que soit l'élément descendant de ce dernier sur lequel l'événement a effectivement eu lieu. Dans notre exemple précédent, le clic sur le lien aurait d'abord été géré par le gestionnaire d'événement de l'élément body, puis celui du div, puis celui du paragraphe, enfin celui du lien.

>Retour à la TdM

4. Exemple

L'exemple suivant montre la remontée et descente des événements dans la structure du DOM

>Retour à la TdM

III. Comment appeler un événement?

1. Par un attribut HTML

a. Rappels

Des événements sont liés à beaucoup d'éléments HTML. On peut accéder à ces événements par un simple appel à un attribut décrivant la nature de l'événement (c'est ce que nous avons fait tout au long des cours précédents). Le nom de cet attribut se construit en préfixant par on le nom de l'événement. L'exemple suivant montre comment utiliser un attribut pour définir un événement.

<span style="background-color: yellow" onmouseover="this.style.backgroundColor='black" onmouseout="this.style.backgroundColor='yellow"> Un élément avec un gestionnaire de base.</span>

En fait, tout se passe comme si, lors de l'appel au code JavaScript dans chaque attribut, le navigateur créait dynamiquement une fonction anonyme contenant le code inscrit dans l'attribut. L'exemple suivant montre comment lancer une boîte d'alerte affichant le contenu de l'attribut onclick de l'élément sur lequel on a cliqué.

<span style="background-color:yellow;" onclick="alert(this.onclick)"> Exemple de contenu avec un événement <code>click</code>.</span>

Le code précis de la fonction dépend du navigateur utilisé, mais le principe reste le même. Cette fonction est appelée le gestionnaire d'événement.

Attention, certains événements ne sont pas applicables à n'importe quel élément. Par exemple, les événements focus et blur (qui désignent respectivement l'action de sélectionner un élément par la souris ou l'emploi de la touche Tabulation, et l'action de le désélectionner) ne sont applicables qu'aux champs de formulaire et aux liens.

b. Compatibilité inter-navigateurs

Netscape et Mozilla créent une fonction portant le nom de l'événement et utilisant un unique argument, event, qui est l'objet événement passé comme paramètre au gestionnaire d'événements.

function onclick(event){
alert(this.onclick) ;
}

Internet Explorer utilise une fonction anonymous. Il ne suit pas le standard, et donc aucun argument n'est défini (nous y reviendrons plus loin).

function anonymous(){
alert(this.onclick) ;
}

Opera utilise une fonction anonymous avec un argument event.

function anonymous(event){
alert(this.onclick) ;
}

c. Les attributs possibles

N'importe quel attribut n'est pas utilisable avec n'importe quel élément.

d. Limitations

Un inconvénient majeur de cette méthode est de mêler le code HTML avec le code Javascript. Cela complique la maintenance de l'un comme de l'autre.

>Retour à la TdM

2. Par l'utilisation d'une propriété d'un élément

Afin de pouvoir séparer un peu plus le traitement d'un événement du code HTML auquel il s'applique, il est possible d'accéder aux événements en ne modifiant que le code Javascript. Si un objet-élément a été précédemment identifié (par une méthode getElementById, par exemple), alors l'événement visé est accessible comme une simple propriété de l'objet élément. L'exemple suivant montre comment utiliser une propriété de l'objet-élément pour définir un événement.

<span id="exemple1">Exemple avec un événement souris.</span>

et le code JavaScript associé :

document.getElementById('exemple1').onmouseover = miseEnGras ;
document.getElementById('exemple1').onmouseout = normal ;

function miseEnGras(event){
  this.style.fontWeight="bold" ;
  this.style.color="red" ;
}

function normal(event){
  this.style.fontWeight="normal" ;
  this.style.color="" ;
}

Il est à remarquer qu'on affecte à la propriété onmouseover la valeur miseEnGras, autrement dit, le nom de la fonction sans les parenthèses. Si on avait écrit miseEnGras(), au moment de l'affectation de l'événement à l'objet-élément, la fonction aurait été évaluée. En affectant le nom de la fonction, en fait on affecte un objet de type function à l'événement, ce qui reproduit le mécanisme interne d'affectation d'un gestionnaire à un événement vu plus haut.

Exercice 1. Utilisation des propriétés des éléments

Énoncé
Correction

>Retour à la TdM

3. Par des « event listeners »

a. Principe

Utiliser une propriété pour affecter un événement à un élément n'est pas la manière la plus « orientée objet » de coder. Il est ainsi difficile pour le code JavaScript de se manipuler lui-même afin de modifier le gestionnaire attaché à un élément particulier.

Un moyen plus respectueux d'une conception orientée objet est d'ajouter des « event listeners » aux éléments. Cela offre en outre l'avantage de permettre le rattachement d'un gestionnaire à plusieurs événements (chose qui, certes, n'était pas interdite par la méthode précédente), mais aussi de facilement affecter plusieurs gestionnaires en réponse à un même événement, sur un élément donné, en fonction du contexte et/ou de ce qui s'est passé. Cela permet aussi de spécifier un comportement différent lors du « cascadage » des événements d'un élément à son parent -ou à un de ses enfants. Nous y reviendrons. Enfin, il est possible d'affecter des événements à n'importe quel nœud de l'arborescence, y compris un nœud de type texte. Pour mémoire, comme un nœud de ce type ne comporte pas d'attribut, il ne possède pas par exemple de propriété onmouseover.

La syntaxe de base est la suivante :

noeud.addEventListener(eventTypefonctionuseCapture) ;
noeud.removeEventListener(eventTypefonctionuseCapture) ;

eventType est un événement prédéfini, comme mouseover ou click (il reprend le même nom que celui de l'attribut correspondant, mais sans le "on"), fonction est le nom de la fonction gérant l'événement, et useCapture un booléen qui spécifie dans quelle phase du flot d'événement la fonction sera appelée.

b. Exemple

Par exemple, pour continuer avec le même cas que précédemment, on aurait pu écrire

var el=.document. getElementById("exemple1") ;
el.addEventListener("mouseover"miseEnGras, false) ;
el.addEventListener("mouseout"normal, false) ;

Des gestionnaires supplémentaires auraient pu tout aussi bien être affectés à cet élément :

el.addEventListener("mouseover"autreGestionnaire, true) ;
el.addEventListener("mouseover"etUnTroisieme, false) ;

De même, on peut les supprimer...

el.removeEventListener("mouseover"autreGestionnaire, true) ;
el.removeEventListener("mouseout"etUnTroisieme, false) ;

Il faut spécifier le même useCapture que lors de l'ajout du gestionnaire. En effet...

el.addEventListener("mouseover"miseEnGras, true) ;
el.addEventListener("mouseover"miseEnGras, false) ;

... crée deux event listeners uniques, appelant certes la même fonction, mais actifs lors de phases différentes.

c. Le cas Internet Explorer

Internet Explorer jusqu'à la version 8 incluse, et Opera jusqu'à la version 5 incluse ne supportent pas les event listeners. Internet Explorer 9 les supporte cependant. Internet Explorer fournit les méthodes attachEvent et detachEvent qui permettent d'affecter ou de détacher plusieurs gestionnaires pour un même événement. Par exemple :

var el = .document. getElementById("sample1") ;
el.attachEvent("onmouseover", highlight) ;
el.attachEvent("onmouseover", highlight2) ;
el.attachEvent("onmouseover", highlight3) ;
el.attachEvent("onmouseout", normal) ;
...
el.detachEvent("onmouseover", highlight2) ;
el.detachEvent("onmouseover", highlight3) ;

Plusieurs remarques s'imposent :

Plusieurs développeurs ont mis au point des librairies de méthodes JavaScript, compatibles avec un maximum de navigateurs. Elles complètent des manques (par exemple elles ajoutent souvent une méthode getElementsByClassName), ou bien pallient des bogues. Elles permettent de manipuler les événements de manière totalement transparente, sans faire appel à de multiples tests pour tenir compte de plusieurs navigateurs.

Exercice 1. Utilisation simple d'un gestionnaire d'événement

Énoncé
Correction

>Retour à la TdM

IV. L'objet event

1. Propriétés et méthodes générales

a. Le standard

Nous avons déjà signalé qu'à tout appel de gestionnaire d'événement, un objet de type événement est passé en argument à la fonction. Plusieurs propriétés décrivent cet objet et son état. On peut les utiliser, notamment, afin de déterminer d'où est issu l'événement, et à quelle étape précise de son « voyage » il en est (montée ou descente...). Il est également possible de l'intercepter, et de faire en sorte qu'il cesse sa propagation.

Nom de la propriété

Description

bubbles

Un booléen indiquant si l'événement remonte l'arborescence ou non.

cancelable

Un booléen indiquant si l'événement peut être annulé.

currentTarget

Le nœud auquel est affecté le gestionnaire d'événement.

eventPhase

Un entier indiquant l'étape où l'événement se trouve dans le flot : il vaut soit CAPTURING_PHASE(1), soit AT_TARGET(2), soit BUBBLING_PHASE(3).

target

Le nœud d'où est parti l'événement.

timeStamp

L'heure à laquelle l'événement a eu lieu.

type

Une chaîne de caractères donnant le type d'événement, comme "mouseover" ou "click", etc.

Nom de la méthode

Description

preventDefault()

Peut être utilisé pour annuler l'événement. Cela empêche le navigateur de procéder à l'action par défaut pour l'événement, comme par exemple charger une URL quand un lien a été cliqué. Attention, l'événement va continuer sa propagation le long de l'arbre.

stopPropagation()

Coupe le flot de l'événement. Cette méthode peut être utilisée lors de la phase de remontée ou de descente.

Table 1. Propriétés et méthodes de l'objet event.

b. Internet Explorer

Les versions d'Internet Explorer inférieures ou égales à 8 ne supportent pas le modèle event du W3C. Au lieu de passer un argument de type event à la fonction, ces versions recourent à un objet global, window.event, qui contient le même genre d'informations.

Malheureusement, les propriétés et méthodes de cet objet ne suivent pas les mêmes conventions de notation que celles du W3C.

Standard W3C

Internet Explorer window.event

Remarques

currentTarget

aucun

Voir ci-dessous.

eventPhase

aucun

Ne s'applique pas à Internet Explorer.

target

srcElement

Le nœud d'où est issu l'événement.

timeStamp

aucun

Non applicable à Internet Explorer.

type

type

Même fonction que dans le standard.

preventDefault()

returnValue

Propriété à mettre à false pour stopper tout traitement par défaut de l'événement.

stopPropagation()

cancelBubble

Propriété à mettre à true pour stopper la remontée de l'événement.

Table 2. Équivalents Internet Explorer ≤8 pour les propriétés et méthodes de l'objet Event.

Afin d'obtenir un équivalent de la propriété currentTarget avec Internet Explorer ≤8, il faut utiliser le mot-clef this comme argument lors de l'appel au gestionnaire :

<cite onmouseover="monGestionnaire(event, this)"> <a href="http://www.paroles.net/texte/18608/artis/1005">Mon légionnaire</a>
</cite>

function monGestionnaire(eventtoto){
alert("Il sentait bon le sable chaud")
}

Cette possibilité n'existe toutefois pas lorsque l'on utilise la méthode propriétaire attachEvent pour allouer un gestionnaire d'événement.

>Retour à la TdM

2. Événements souris

a. Propriétés

Les événements souris sont :

Nom de la propriété

Description

altKey, ctrlKey, metaKey, shiftKey

Booléens. Ils valent true si la touche correspondante était enfoncée quand l'événement souris a été activé.

button

Un entier indiquant quel bouton de souris était enfoncé : 0=gauche, 1=milieu, 2=droit. Une souris à un seul bouton (type Mac) ne retournera que la valeur 1 ; une souris à deux boutons retournera les valeurs 0 ou 2.

clientX, clientY

Donne les coordonnées du pointeur de la souris quand l'événement a eu lieu, par rapport à la fenêtre du navigateur effectivement disponible (hors barre de défilement).

relatedTarget

Sur un mouseover, cette propriété indique le nœud que vient de quitter la souris ; sur un mouseout, il s'agit du nœud sur lequel la souris vient de se placer. Cette propriété n'est pas supportée parInternet Explorer : voir ci-après.

screenX, screenY

Donne les coordonnées du pointeur de la souris quand l'événement a eu lieu, par rapport à l'écran.

Table 3. Liste des propriétés de l'objet event liées à la souris.

b. Remarques

Les événements souris sont toujours attachés à l'élément de plus bas niveau dans l'arborescence. Par exemple, cela sera le lien si l'utilisateur a cliqué sur un lien inclus dans un paragraphe.

Lors du clic, trois événements ont lieu, toujours dans le même ordre : mousedown, mouseup et click. Ces événements sont toujours traités successivement, le traitement de chacun d'eux ne pouvant débuter que si celui du précédent est terminé.

c. Compatibilité inter-navigateurs

Opera considère -de manière erronée- que les propriétés clientX et et clientY sont relatives à l'origine de la page (autrement dit, la largeur des barres de défilement est incluse).

Les conventions d'Internet Explorer diffèrent encore des standards. Voici une liste des équivalences :

Exercice 1. Manipulation des gestionnaires d'événements

Énoncé
Correction

Exercice 2. Manipulation des gestionnaires d'événements (suite)

Énoncé
Correction

>Retour à la TdM

3. Événements clavier

a. Introduction

Le DOM niveau 2 n'inclut pas de consigne à propos des événements clavier. Cependant, la recommandation HTML permet l'utilisation de plusieurs attributs, et par conséquent des événements correspondants. Ce sont les événements keydown, keypress et keyup, indiquant respectivement qu'une touche a été enfoncée, appuyée ou relevée. Dans le cas de la frappe d'une touche clavier, ces trois événements sont produits et gérés dans cet ordre. Netscape (et par souci de rétro-compatibilité, FireFox) et Internet Explorer ont inclus des propriétés afin de permettre la gestion des événements clavier, et notamment le codage des touches du clavier éventuellement enfoncées. Heureusement, de fortes similitudes existent.

Dans FireFox et Netscape, le code ASCII de la touche enfoncée est donné par la propriété keyCode quand l'événement keydown a été activé, et par la propriété charCode pour l'événement keypress.

Internet Explorer stocke le code Unicode de la touche dans la propriété keyCode pour chacun des trois types d'événements.

Dans les deux cas, les événements keydown et keypress sont activés dès qu'une touche quelconque est enfoncée (y compris une touche de fonction, ou une touche Ctrl, Shift ou Alt). Pour saisir une combinaison de touches comme Shift-a, par exemple, il est nécessaire d'avoir recours à l'événement keypress.

Voici quelques exemples de codes renvoyés par ces propriétés pour Internet Explorer et FireFox. Il ne faut pas oublier que certaines combinaisons de touches sont interceptées par le système d'exploitation (par exemple souvent Ctrl-n pour ouvrir une nouvelle fenêtre). De plus, certaines extensions sous Firefox redéfinissent leurs propres raccourcis clavier. Selon le système et le navigateur, vous aurez accès à ces combinaisons de touches ou non.

b. Internet Explorer

Touche(s)

keydown

keyup

keypress

a

keyCode=65

keyCode=65

keyCode=97

shift

keyCode=16 et shiftKey=true

keyCode=16 et shiftKey=false

Inapplicable.

shift-a

L'événement est actif, quelle que soit la touche enfoncée.

L'événement est actif, quelle que soit la touche enfoncée.

keyCode=65 et shiftKey=true

z

keyCode=90

keyCode=90

keyCode=122

ctrl

keyCode=17 et ctrlKey=true

keyCode=17 et ctrlKey=true

Inapplicable.

ctrl-z

L'événement est actif, quelle que soit la touche enfoncée.

L'événement est actif, quelle que soit la touche enfoncée.

keyCode=26 et ctrlKey=true

Table 4. Exemples d'événements clavier pour Internet Explorer.

c. Netscape et FireFox

Touche(s)

keydown

keyup

keypress

a

charCode=0, keyCode=65, which=65

charCode=0, keyCode=65, which=65

charCode=97, keyCode=0, which=97

shift

charCode=0, keyCode=16, shiftKey=true

charCode=0, keyCode=16, shiftKey=false

Inapplicable.

shift-a

L'événement est actif, quelle que soit la touche enfoncée.

Chaque touche renvoie un événement séparé ; de plus, shiftKey=true pour la touche a.

charCode=65, keyCode=0, which=65, shiftKey=false (!)

z

charCode=0, keyCode=90, which=90

charCode=0, keyCode=90, which=90

charCode=122, keyCode=0, which=122

ctrl

charCode=0, keyCode=17, ctrlKey=true

charCode=0, keyCode=17, ctrlKey=false

Inapplicable.

ctrl-z

L'événement est actif, quelle que soit la touche enfoncée.

Chaque touche renvoie un événement séparé ; de plus, ctrlKey=true pour la touche z.

charCode=122, keyCode=0, which=122, ctrlKey=true

Table 5. Exemples d'événements clavier pour Netscape et Mozilla.

>Retour à la TdM

4. Événements divers

On consultera la liste détaillée des nombreux événements sur le site du Mozilla Developer Network. Voici une petite sélection d'événements particulièrement intéressants...

b. Événements liés à la manipulation de la fenêtre

resize est activé quand la fenêtre est redimensionnée, scroll quand on fait défiler le document, ou bien un élément.

c. Évenements liés au drag&drop

Ces événements sont nécessaires à l'utilisation de l'attribut draggable de HTML5. Il s'agit de drag et drop au début et à la fin de la phase de drag&drop, dragstart, dragend quand le drag&drop est terminé, dragenter qui est activé quand on entre au-dessus d'une zone où l'objet en cours de déplacement peut être déposé, dragleave quand on sort d'une zone où l'objet peut être déposé, et dragover au survol.

d. Événements liés au copier/coller

Il s'agit, très classiquement, de copy, cut et paste.

e. Événements liés aux animations CSS

animationstart et animationend sont activés récemment au début et à la fin d'une animation, tandis qu'animationiteration est activé à chaque boucle.

f. Événements liés à la connexion Internet

Ces événements sont particulièrement utiles quand on doit gérer des Progressive Web Applications : online et offline sont des événements qui sont activés quand le navigateur obtient, ou perd, une connexion au réseau.

>Retour à la TdM

Historique de ce document

Conditions d'utilisation et licence

Creative Commons License
Cette création est mise à disposition par Gilles Chagnon sous un contrat Creative Commons.

Retour au menu