Skip to content

Gestion d'évènements

Les événements sont des éléments fondamentaux dans l’interactivité d’une application. Ils permettent d’observer l’ensemble des actions effectuées sur le navigateur, en premier lieu par le ou les utilisateurs du site : le clic sur un bouton, le mouvement de la souris, la saisie dans un champ de formulaire ou le changement de résolution de l’interface sont autant d’événements auxquels une application peut réagir.

Dans ce chapitre, nous verrons plus en détail comment réagir à un événement dans une application utilisant un framework JS.

Evènements en Javascript

Définition

Les navigateurs permettent d’observer les événements au niveau de chaque élément du DOM. En JavaScript, il est possible de désigner une opération qui devra être effectuée à chaque occurrence d’un type d’événement particulier, sur un élément spécifique du DOM :

html
<button id="my-button">Click me</button>
js
const button = document.getElementById('my-button');
button.addEventListener('click', () => {
    alert('Button clicked !');
});

La méthode addEventListener, disponible sur chaque élément du DOM, prend en premier argument le type d’événement à observer et, en second, la fonction callback à exécuter à chaque occurrence de l’événement.

TIP

💡 Une syntaxe plus concise est possible en utilisant des attributs directement dans les balises HTML : <button onclick="alert('Button clicked!')">Click me</button>

Les événements supportés peuvent différer en fonction du type de balise HTML. Les balises de formulaire (<input />, <textarea />, <select />) disposent d'événements permettant d'observer lorsque la valeur du champ a été modifiée (input) ou lorsque l’utilisateur a fini d’interagir avec l’élément (change). Vous pouvez retrouver la liste des différents événements dans la documentation MDN.

Propagation

Il est important de comprendre que l’observation des événements se propage au sein du DOM, des éléments enfants vers les éléments parents. Prenons cet exemple:

html
<div onclick="alert('Div clicked !')">
  <button onclick="alert('Button clicked !')">Click me</button>
</div>

Si l’on clique sur le visuel associé à la balise <button>, l’événement de clic sera observé sur la balise, puis se propagera à l’élément parent, ici la balise <div>, qui observera à son tour l’événement.

La fonction associée au <button> s’exécutera donc en premier, avant que la fonction associée à la balise <div> s’exécute à son tour, et ainsi de suite pour les éléments parents suivants.

NOTE

Notez qu’il est possible d’empêcher la propagation d’un événement à un certain niveau du DOM en utilisant la méthode event.stopPropagation() dans une fonction associée à l’événement, au niveau de la balise devant arrêter la propagation. (plus d'infos sur MDN).

Evènements dans un composant

Déclaration

Comme pour l’usage des variables dans le code HTML, les frameworks JavaScript simplifient la création d’événements en permettant d’associer directement une fonction JavaScript à l’événement d’une balise HTML du même composant :

jsx
export default function App() {
  const sendAlert = () => {
    alert("Button clicked !")
  };

  return (
     <button onClick={sendAlert}>Click me</button>
  );
}

Les accolades au niveau de l’attribut de balise signifient que du code JavaScript est introduit, et non un simple texte. Notez la majuscule "C" dans l’attribut onClick, qui le différencie de l’attribut JavaScript natif. Elle est obligatoire pour que l’association fonctionne. Cette logique se décline pour tous les types d'évènements (onChange, onSubmit, onMouseEnter, onFocus).

Objet Event

En JavaScript, les fonctions associées à un événement reçoivent en argument un objet fournissant des informations contextuelles sur l'événement déclenché. Le contenu de l'objet diffèrent en fonction du type d'évènement (plus d'info sur MDN)

Cet objet est également transmis en argument des fonctions associées dans les composants, avec les mêmes propriétés

jsx
export default function App() {
  const sendAlert = (event) => {
    alert(event.target.innerText); // => "Click me"
  };

  return (
    <div>
      <button onClick={sendAlert}>Click me</button>
    </div>
  );
}

Si l'on souhaite fournir d'autres paramètres à la fonction, il est possible de transmettre plus explicitement l'objet event (ou de l'omettre volontairement) :

jsx
export default function App() {
  const sendAlert = (prefix, event) => {
    // => Button text: Click me
    alert(prefix + event.target.innerText); 
  };

  return (
    <div>
      <button onClick={(e) => sendAlert('Button text: ', e)}>
        Click me
      </button>
    </div>
  );
}

TIP

💡 En JavaScript, il est possible de créer une fonction sans déclarer l'objet event en argument et de l’associer à un événement. L'objet event sera alors automatiquement ignoré.

🐶 Dog Center

Pour notre galerie de chiens, il nous a été demandé d’ajouter une nouvelle fonctionnalité : afin de permettre aux utilisateurs de mieux choisir le chien qu’ils souhaitent adopter, le chenil aimerait proposer un enregistrement audio du chien, que l’on pourrait écouter au niveau des DogCard, présentant les chiens dans la galerie, en cliquant sur un bouton.

Pour cela, une URL vers un fichier audio a été ajoutée aux données de chaque chien du chenil :

js
export default [
  {
    id: 1,
    name: "Buddy",
    age: 3,
    breed: "Golden Retriever",
    pictureUrl: "https://dog-center.com/api/dogs/1.png",
    soundUrl: "https://dog-center.com/api/dogs/1.mp3"
  },
  ...
]

Au niveau de la galerie, nous pouvons ajouter la transmission de la donnée soundUrl au composant DogCard à l’aide d’une nouvelle props :

jsx
import './App.css';
import dogsData from './dogsData';
import DogCard from './components/DogCard';

export default function App() {
  return (
    <div>
      <h1>Dog Center</h1>
      <div id="dog-gallery">
        { dogsData.map((dog) => (
        <DogCard 
            key={dog.id}
            name={dog.name}
            age={dog.age}
            breed={dog.breed}
            pictureUrl={dog.pictureUrl}
            soundUrl={dog.soundUrl}
          />
        ))}
      </div>
    </div>
  );
}

Enfin, dans le composant DogCard, nous pouvons déclarer la nouvelle props soundUrl et définir une fonction lançant l’enregistrement audio. On peut ensuite ajouter un bouton à nos DogCard qui exécutera la fonction lorsque la balise identifiera un clic de l’utilisateur :

jsx
import './DogCard.css'

export default function DogCard({ 
    name,
    age,
    breed,
    pictureUrl,
    soundUrl
  }) {
  const audio = new Audio(soundUrl)

  const playDogSound = () => {
    audio.currentTime = 0
    audio.play()
  }

  return (
    <div id="dog-card">
      <img
        id="dog-picture"
        src={pictureUrl}
        alt="Dog"
      />
      <div id="dog-description">
        <h3>{name}</h3>
        { 
          age && 
          <p className="dog-description-line">Age: {age} years</p> 
        }
        <p className="dog-description-line">Breed: {breed}</p>
        <button onClick={playDogSound}>Listen</button>
      </div>
    </div>
  );
};

Conclusion

Les frameworks JavaScript empruntent beaucoup à la syntaxe native de JavaScript pour permettre la gestion des événements. En permettant directement de se référer au code JavaScript, ils offrent une gestion des événements simple et claire au sein des composants.

Dans le prochain chapitre, nous allons voir que ces événements peuvent modifier l’état des données d’un composant de façon réactive et ainsi, très simplement, modifier l’apparence du composant.