Réagir aux événements survenant sur les objets du DOM

1. Introduction

Les interactions que l'utilisateur peut avoir avec un document Web chargé dans un navigateur vont produire des événements. Il existe différents types d'événements selon les actions de l'utilisateur. Voici quelques-uns de ces événements :

Pour une liste plus complète des événements existant, voir w3schools.

Lorsqu'un événement survient, il est toujours associé à l'élément du DOM concerné par l'événement. Par conséquent, si une fonction a été associée à ce type d'événement sur ce type d'élément du DOM, l'interpréteur javascript va l'exécuter ce qui va permettre de "réagir" à l'événement. Par exemple, suite au clic sur un bouton, on va pouvoir afficher un message particulier dans la page.

Voici un petit exemple d'un tel comportement :

Réagir à un clic sur un bouton

Texte au chargement

Ainsi, pour réagir aux actions de l'utilisateur, il faut pouvoir, en javascript :

  1. retrouver l'objet représentant l'élément du DOM associé à l'événement (dans l'exemple, l'objet associé à la balise <input>)
  2. définir le traitement que l'on souhaite effectuer en réaction à l'événement (dans l'exemple, fonction qui modifie le contenu du paragraphe)
  3. associer le traitement à l'événement adéquat et à l'objet du DOM qui nous intéresse (dans l'exemple, l'événement concerné est click).

2. Retrouver l'objet représentant un élément du DOM

En javascript, le DOM est représenté grâce à un arborescence d'objets. Les différents types d'objets permettant de représenter le DOM sont donnés ici On vous propose également ce petit résumé :

.

La façon la plus simple de retrouver directement un élément du DOM consiste à utiliser la méthode getElementById(identifiantElement) de l'objet document construit pour vous par le navigateur.

Ainsi, dans le document html suivant :

<html>
<body>
<h1 id="titre">Ma page</h1>
<p id="p1">Premier paragraphe</p>
<p id="p2">Deuxième paragraphe< <strong>avec une partie en gras</strong> /p>
</body>
</html>

Pour récupérer l'objet représentant le deuxième paragraphe on pourra écrire :

var para2 = document.getElementById("p2")

D'autres méthodes peuvent être utiles pour retrouver des noeuds du DOM. Elles figurent sur le schéma ci-dessus. En voici la liste :

Vous trouverez sur le site w3schools, une page résumant les différentes propriétés et fonctions permettant de naviguer facilement dans le DOM.

Depuis la norme ECMAS2015, une nouvelle façon de retrouver des éléments du DOM en utilisant des sélecteurs CSS est apparue. Elle consiste à utiliser, entre autres, la fonction querySelector sur l'objet document ou sur un noeud du DOM en lui passant en paramètre, un sélecteur CSS tel qu'un nom de balise HTML, un nom de classe CSS, un identifiant CSS ou encore une combinaison de ces éléments. Cela permet de récupérer en un appel un noeud du DOM sans identifiant et sans pour autant naviguer dans le DOM jusqu'au noeud recherché.

On peut trouver ici un exemple d'utilisation de cette fonction (en commentaires, figurent les versions sans cette fonction pour pouvoir comparer).
Le site MDN propose une page décrivant les possibilités de cette fonction et son mode d'utilisation.

Exercice 1

NB : Lorsque le navigateur exécute un code javascript dans une page HTML, les éventuelles erreurs générées lors de cette exécution sont visibles dans la console. Il faut donc la laisser ouverte dès le chargement de la page pour pouvoir voir les messages d'erreurs.

On vous donne le fichier exoEvenements1.html, le fichier exoEvenements1.js et le fichier css associé style.css. Comme vous pouvez le constater, lorsque l'on passe le pointeur de la souris sur le premier paragraphe, celui-ci est mis en surbrillance. Le choix de l'élément du DOM mis en surbrillance est effectué dans la fonction rechercherElement du fichier javascript (vous pouvez ignorer le reste de ce fichier pour le moment).

Sans modifier le fichier html, modifier la fonction rechercherElement pour qu'elle réponde successivement aux 3 problèmes suivants :

  1. C'est le troisième paragraphe qui doit être mis en surbrillance quand la souris arrive dessus ;
  2. C'est le deuxième paragraphe qui doit être mis en surbrillance quand la souris arrive dessus ;

    NB: Ce paragraphe n'a pas d'identifiant, on ne peut donc pas utiliser getElementById cette fois. A la place, on va utiliser les propriétés de Node qui permettent de se déplacer dans l'arbre du DOM. Ici on pourra commencer par utiliser parentNode sur le noeud du DOM correspondant au premier paragraphe ce qui permettra de récupérer le noeud correspondan à la balise body. Dans un deuxième temps, on pourra utiliser childNodes sur ce noeud pour récupérer l'ensemble des noeuds fils de la balise body. Cette propriété est une collection qui peut être accédée par des indices comme pour les tableaux classiques. Il ne reste plus qu'a déterminer le numéro du noeud fils correspondant au second paragraphe. Attention à ne pas oublier les sauts de ligne dans votre décompte !

  3. C'est le mot "joli" qui doit être mis en surbrillance quand la souris arrive dessus ;

    NB: : vous pouvez utiliser ici les mêmes fonctions que dans la question précédente

3. Définir le traitement que l'on souhaite effectuer

Le traitement que l'on souhaite effectuer lorsqu'un événement survient doit être défini via une fonction (appelée souvent handler) qui prend un paramètre de type Event en paramètre (paramètre que l'on appelle classiquement event). Ce paramètre est un objet représentant l'événement qui a été déclenché. Pour plus de détails sur ce paramètre, voir MDN

Dans une fonction handler, le mot-clef this désigne le noeud du DOM auquel est associé le handler qui est en train de s'exécuter. Ce noeud correspond la plupart du temps à l'élément qui a déclenché l'événement.

NB : : Lorsqu'un événement est déclenché, il est propagé en descendant puis en remontant dans l'arbre du DOM. Cela doit être pris en compte lorsque l'on associe plusieurs handlers pour le même événement mais pour différents noeuds du DOM. Cet aspect ne sera pas traité dans le cadre de cette formation. Pour en savoir plus : MDN (partie "bubbling and capturing")

Exercice 2

Dans l'exercice précédent, le traitement effectué lorsque la souris arrive sur l'élément du DOM choisi est défini dans la fonction surbrillance. Modifier cette fonction pour produire les comportements suivants :

  1. Quand la souris arrive sur l'élément choisi (le premier paragraphe par exemple), le paragraphe est mis sur fond jaune et le texte "arrivée détectée" est affiché dans la console ;
  2. Quand la souris arrive sur l'élement choisi, une fenêtre d'alerte affiche le message "Danger !" ;

    NB : on pourra utiliser la fonction alert(string) de l'objet window pour générer la fenêtre d'alerte.
    Plus d'informations sur cette fonction ici.

  3. Quand la souris arrive sur l'élément choisi, l'objet représentant l'élement du DOM ciblé est affiché dans la console.

4. Associer le traitement au bon événement

Pour associer un traitement à un événement émis par un élement du DOM, il faut utiliser la méthode addEventListener(nomEvénement, nomTraitement) sur l'élément du DOM en question. Le nom de l'évément est une chaîne pouvant correspondre à un des noms d'événements donnés en introduction. Le nom du traitement est celui d'une fonction (le handler) définissant le traitement à effectuer (voir partie précédente).

En appelant plusieurs fois la méthode addEventListener, il est tout à fait possible d'associer plusieurs traitements à un même événement survenant sur un seul et même élément du DOM.

NB: Dans des versions plus anciennes de javascript, pour installer un gestionnaire d'événements, au lieu d'utiliser la fonction addEventListener, on utilisait des attributs HTML portant le nom des événements à gérer au niveau de la balise HTML où cet attribut apparait. Cela est illustré dans l'exemple fourni ici qui est une ré-écriture de l'exemple apparaissant au début de cette page (clic sur un bouton).
Cette façon de faire est encore présente, sur Internet notamment. Elle est pourtant devenue obsolète. Elle présente notamment le gros inconvénient de mélanger au sein du même fichier du HTML et du javascript ce qui nuit considérablement à la lecture et à la maintenance du code.

Exercice 3

Dans l'exercice précédent, les associations (element DOM,evenement,traitement) sont établies dans la fonction init. Pour gérer l'effet de mise en surbrillance au survol de la souris, il a fallu associer des traitements à 2 événements différents :

Modifier la fonction init pour répondre à chacun des cas suivants :

  1. Quand la souris arrive sur l'élément, celui-ci passe en surbrillance et reste dans cet état. Quand l'utilisateur clique sur l'élément en surbrillance, celui-ci repasse en mode normal ;
  2. Quand l'utilisateur clique sur l'élément choisi. celui-ci passe en surbrillance s'il ne l'était pas encore et repasse en mode normal sinon.

    NB : dans la fonction surbrillance, on pourra utiliser la fonction getAttribute pour connaitre la valeur de l'attribut class de l'élément du DOM sur lequel on a cliqué de manière à pouvoir ensuite mettre à jour correctement avec la fonction setAttribute la valeur de cet attribut.

5. Associer les événements au bon moment

Afin de gagner du temps (les échanges via le réseau sont quelques fois lents...), les navigateurs web essaient de paralléliser les choses. Et notamment, ils exécutent le code javascript dès qu'il est reçu, même si la page n'est pas encore complètement chargée ou complètement construite. Du coup, la recherche d'un élément du DOM (via par exemple un getElementById) risque d'être effectuée avant que celui-ci n'ait été construit.

Pour éviter cela, on va attendre que le navigateur ait complètement construit le DOM. Et pour ce faire, fort heureusement, un événement "DOMContentLoaded" est émis au niveau du document lorque c'est fait. Il ne nous reste plus qu'à associer un traitement à cet événement-là sur le DOM. Voilà pourquoi, dans l'exemple qui sert de support aux exercices précédents, l'association des traitements aux objets du DOM (et donc la recherche de ces derniers) est faite dans une fonction init(event), qui est associée, dans la première ligne du fichier à l'événement "DOMContentLoaded" généré par le document (une autre solution, moins judicieuse toutefois, est d'associer cette fonction d'initialisation à l'événement "load" de l'objet "window").

6. Pour aller plus loin : Utiliser les événements pour valider un formulaire

Un des rôles de Javascript est de valider les données d'un formulaire avant que celui-ci ne soit envoyé au serveur. Le retour à l'utilisateur est alors bien plus rapide que si on attendait que le serveur valide la saisie.

Pour ce faire, plusieurs événements peuvent être utiles :

Exercice 4

Un formulaire vous est proposé dans le document exoEvenements4.html. Ce formulaire permet de saisir les données (fin,debut,pas) nécessaires à l'utilisation de la fonction suite que vous avez écrite précédemment. Dans un premier temps, on ne va pas générer la suite de nombres en fonction de ce qui est saisi. L'objectif ici est de valider le formulaire c'est-à-dire :

Lorsque l'on clique sur le bouton generer : Pour mettre ce comportement en place, vous devez compléter le fichier exoEvenements4.js