Back

Guide du développeur sur les événements personnalisés JavaScript

Guide du développeur sur les événements personnalisés JavaScript

Vous avez construit une architecture de composants propre, mais maintenant les différentes parties doivent communiquer entre elles sans créer de couplage fort. Les événements DOM natifs gèrent bien les interactions utilisateur, mais qu’en est-il des signaux spécifiques à votre application ? Les événements personnalisés JavaScript résolvent ce problème de manière élégante.

Ce guide couvre la création et le déclenchement d’événements personnalisés, la transmission de données structurées via la charge utile detail de CustomEvent, et l’utilisation d’EventTarget comme bus d’événements léger. Vous apprendrez également comment fonctionnent les événements personnalisés dans les Web Components et comment les événements personnalisés du Shadow DOM se propagent à travers les frontières.

Points clés à retenir

  • Utilisez le constructeur CustomEvent avec la propriété detail pour transmettre des données structurées via les événements
  • L’interface EventTarget fonctionne de manière autonome, permettant des modèles pub/sub légers sans éléments DOM
  • Définissez bubbles: true pour les événements qui doivent se propager dans l’arborescence DOM
  • Utilisez composed: true pour permettre aux événements de franchir les frontières du Shadow DOM dans les Web Components

Création et déclenchement d’événements personnalisés

Le constructeur CustomEvent est la méthode moderne pour créer des événements personnalisés. Oubliez initCustomEvent — c’est une API héritée dont vous n’aurez pas besoin dans les navigateurs modernes.

const event = new CustomEvent('user-login', {
  detail: { userId: 123, timestamp: Date.now() },
  bubbles: true,
  cancelable: true
})

document.querySelector('#app').dispatchEvent(event)

Trois options sont importantes ici :

  • detail : Votre charge utile structurée (toutes données sérialisables)
  • bubbles : Indique si l’événement se propage dans l’arborescence DOM
  • cancelable : Indique si les écouteurs peuvent appeler preventDefault()

L’écoute fonctionne exactement comme pour les événements natifs :

document.querySelector('#app').addEventListener('user-login', (e) => {
  console.log(e.detail.userId) // 123
})

La charge utile detail de CustomEvent

La propriété detail est ce qui fait briller les événements personnalisés par rapport au constructeur Event de base. Bien que vous puissiez techniquement assigner des propriétés arbitraires à un objet Event après sa création, detail fournit un espace de noms dédié et sans conflit pour vos données.

const cartEvent = new CustomEvent('cart-updated', {
  detail: {
    items: [{ id: 1, qty: 2 }, { id: 3, qty: 1 }],
    total: 59.99,
    currency: 'USD'
  }
})

Les gestionnaires accèdent à ces données via event.detail — propre et prévisible.

EventTarget du DOM comme bus d’événements

Vous n’avez pas besoin d’un élément DOM pour utiliser les événements. L’interface EventTarget fonctionne de manière autonome, ce qui la rend parfaite pour un mécanisme pub/sub léger :

class AppEventBus extends EventTarget {
  emit(eventName, data) {
    this.dispatchEvent(new CustomEvent(eventName, { detail: data }))
  }
  
  on(eventName, handler) {
    this.addEventListener(eventName, handler)
  }
  
  off(eventName, handler) {
    this.removeEventListener(eventName, handler)
  }
}

const bus = new AppEventBus()
bus.on('notification', (e) => console.log(e.detail.message))
bus.emit('notification', { message: 'Hello!' })

Ce modèle de bus d’événements basé sur EventTarget maintient les composants découplés sans dépendances externes. Pour les utilisateurs de TypeScript, vous pouvez typer la charge utile detail en utilisant des génériques : CustomEvent<{ message: string }>.

Remarque : Dans Node.js, EventTarget existe mais CustomEvent n’est disponible comme global que dans Node 19+. L’EventEmitter de Node reste le modèle le plus courant dans cet environnement.

Événements personnalisés dans les Web Components

Les Web Components s’appuient fortement sur les événements personnalisés pour la communication vers l’extérieur. Un composant déclenche des événements tandis que le code parent écoute :

class UserCard extends HTMLElement {
  connectedCallback() {
    this.addEventListener('click', () => {
      this.dispatchEvent(new CustomEvent('user-selected', {
        detail: { id: this.dataset.userId },
        bubbles: true,
        composed: true
      }))
    })
  }
}
customElements.define('user-card', UserCard)

Le parent écoute sans connaître les détails internes du composant :

document.querySelector('user-card').addEventListener('user-selected', (e) => {
  loadUserProfile(e.detail.id)
})

Événements personnalisés du Shadow DOM et l’option composed

Les événements personnalisés du Shadow DOM se comportent différemment selon l’option composed :

  • composed: false (par défaut) : L’événement s’arrête à la frontière de la racine shadow. L’implémentation interne reste cachée.
  • composed: true : L’événement franchit les frontières shadow et remonte dans le light DOM.
// À l'intérieur du shadow DOM
this.shadowRoot.querySelector('button').dispatchEvent(
  new CustomEvent('internal-action', {
    bubbles: true,
    composed: true // Échappe à la frontière shadow
  })
)

Lorsqu’un événement franchit la frontière shadow, event.target est reciblé vers l’élément hôte — les écouteurs à l’extérieur voient le composant, pas sa structure interne.

Utilisez composed: true pour les événements que le code externe doit gérer. Conservez composed: false pour la communication interne du composant.

Une petite remarque sur event.isTrusted : cette propriété indique si l’événement a été généré par le navigateur (action utilisateur) ou par un script. C’est informatif, pas un mécanisme de sécurité — ne vous y fiez pas pour le contrôle d’accès.

Conclusion

Les événements personnalisés JavaScript offrent un moyen indépendant des frameworks pour construire des architectures événementielles faiblement couplées. Utilisez CustomEvent avec une charge utile detail structurée pour les données, exploitez EventTarget comme bus d’événements autonome, et comprenez comment bubbles et composed contrôlent la propagation — en particulier à travers les frontières du Shadow DOM. Ces modèles s’adaptent de la simple communication entre composants aux architectures micro-frontend complexes.

FAQ

La principale différence est la propriété detail. CustomEvent inclut une propriété detail dédiée pour transmettre des données structurées, tandis que le constructeur Event de base ne l'a pas. Bien que vous puissiez ajouter des propriétés à un objet Event après sa création, utiliser CustomEvent avec detail est plus propre et évite les conflits de nommage potentiels avec les propriétés d'événement existantes.

Oui, mais avec des réserves. React ne prend pas en charge nativement les écouteurs d'événements personnalisés dans JSX, vous devrez donc utiliser des refs et attacher manuellement les écouteurs avec addEventListener. Vue gère mieux les événements personnalisés via son système d'événements. Les événements personnalisés fonctionnent mieux lors de la communication entre des composants de framework et des Web Components vanilla ou du code indépendant du framework.

Non. Définissez bubbles à true uniquement lorsque les éléments parents doivent intercepter l'événement. Pour une communication directe où vous déclenchez et écoutez sur le même élément, le bubbling est inutile. Un bubbling excessif peut conduire à ce que des écouteurs non intentionnels interceptent des événements, soyez donc intentionnel quant à la propagation.

Utilisez removeEventListener avec exactement la même référence de fonction que vous avez passée à addEventListener. Les fonctions anonymes ne peuvent pas être supprimées car il n'y a pas de référence à passer. Stockez votre fonction gestionnaire dans une variable ou utilisez le modèle AbortController avec l'option signal pour un nettoyage plus propre.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay