Command Palette

Search for a command to run...

Custom Events: Crea y Despacha Eventos Personalizados

Aprende a crear, despachar y escuchar eventos personalizados en JavaScript usando CustomEvent y dispatchEvent para comunicación entre componentes.

Lectura: 12 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • Los custom events permiten crear eventos personalizados para comunicación entre componentes
  • Usa new CustomEvent() para crear eventos con datos personalizados en la propiedad detail
  • dispatchEvent() dispara el evento en un elemento específico del DOM
  • Los custom events pueden propagarse (bubbling) y ser capturados como eventos nativos
  • Son ideales para desacoplar componentes y implementar arquitecturas basadas en eventos

Introducción a los Custom Events

Los custom events son eventos personalizados que puedes crear y despachar en JavaScript, permitiendo comunicación entre diferentes partes de tu aplicación sin acoplamiento directo. A diferencia de los eventos nativos (click, submit, etc.), los custom events te permiten definir nombres específicos y transferir datos personalizados a través de la propiedad detail. Son ideales para comunicación entre componentes no relacionados, notificaciones de cambios de estado, implementación de plugins e integración con librerías de terceros.

Esta característica es especialmente útil en aplicaciones grandes donde múltiples componentes necesitan comunicarse entre sí. Los custom events siguen el mismo modelo de propagación que los eventos nativos, soportando bubbling y capturing, lo que te permite controlar cómo se propagan a través del DOM.

  • <strong>Desacoplamiento</strong>: Los componentes no necesitan conocerse directamente
  • <strong>Comunicación</strong>: Permite comunicación unidireccional o bidireccional entre módulos
  • <strong>Flexibilidad</strong>: Puedes transferir cualquier tipo de datos en la propiedad detail
  • <strong>Patrón Pub/Sub</strong>: Implementa fácilmente el patrón publicador/suscriptor
  • <strong>Extensibilidad</strong>: Crea tu propia arquitectura basada en eventos

Crear y Despachar Custom Events

Para crear un custom event, usas el constructor CustomEvent() que acepta dos parámetros: el nombre del evento y un objeto de configuración opcional. El objeto de configuración puede incluir la propiedad detail para transferir datos, bubbles para controlar la propagación, y cancelable para permitir que el evento sea cancelado.

Crear un Custom Event Básico

El constructor CustomEvent() crea un nuevo evento personalizado. El primer parámetro es el nombre del evento, que debe ser un string descriptivo en kebab-case (con guiones, como 'producto-agregado' en lugar de 'productoAgregado'), siguiendo las convenciones de eventos nativos. El segundo parámetro es un objeto de configuración opcional donde puedes definir detail (datos personalizados), bubbles (si el evento debe propagarse) y cancelable (si el evento puede ser cancelado).

crear-custom-event.js
Loading code...

Este ejemplo muestra cómo crear y despachar un custom event básico. Primero creamos el evento con new CustomEvent(), especificando el nombre 'producto-agregado' y un objeto de configuración con bubbles: true para que el evento se propague. Luego usamos dispatchEvent() para disparar el evento en el elemento button.

Despachar el Evento con dispatchEvent

El método dispatchEvent() dispara un evento en un elemento específico del DOM. El evento se propaga según la configuración de bubbles y puede ser escuchado por cualquier elemento ancestro si bubbles es true. dispatchEvent() retorna true si el evento no fue cancelado por preventDefault(), y false en caso contrario.

dispatch-event.js
Loading code...

Este ejemplo muestra cómo despachar un custom event desde un botón y escucharlo en un elemento ancestro (div contenedor). El evento se propaga porque bubbles es true. El valor de retorno de dispatchEvent() indica si el evento fue cancelado, lo cual es útil para saber si algún listener llamó a preventDefault().

dispatchEvent es síncrono

dispatchEvent() es síncrono, lo que significa que todos los listeners se ejecutan antes de que la función retorne. Esto te permite saber inmediatamente si el evento fue cancelado o procesado correctamente.

Escuchar Custom Events

Los custom events se escuchan exactamente igual que los eventos nativos, usando addEventListener(). Puedes escuchar el evento en el mismo elemento donde se despacha o en cualquier elemento ancestro si el evento tiene bubbles: true. Los listeners reciben el evento como parámetro, el cual contiene toda la información del evento, incluyendo los datos en event.detail.

Escuchar en el Mismo Elemento

Puedes escuchar un custom event en el mismo elemento donde se despacha usando addEventListener(). Esto es útil cuando el evento es local y no necesita propagarse a otros elementos. El listener recibe el evento como parámetro, y puedes acceder a los datos personalizados a través de event.detail.

escuchar-mismo-elemento.js
Loading code...

Este ejemplo muestra cómo escuchar un custom event en el mismo elemento donde se despacha. El listener accede a los datos del evento a través de event.detail. Es importante notar que el evento se despacha DESPUÉS de agregar el listener, de lo contrario el listener no lo capturará.

Escuchar en Elementos Ancestros

Cuando un custom event tiene bubbles: true, puedes escucharlo en elementos ancestros. Esto permite implementar patrones de delegación de eventos y comunicación entre componentes que no están directamente relacionados. El evento se propaga hacia arriba en el DOM hasta que llega al document.

escuchar-ancestro.js
Loading code...

Este ejemplo muestra cómo escuchar un custom event en un elemento ancestro. El evento se despacha en un botón dentro del contenedor, pero se escucha en el contenedor mismo. Esto es útil para centralizar la lógica de manejo de eventos y evitar agregar listeners a múltiples elementos individuales.

Advertencia: Eventos con bubbles: false

Si un custom event tiene bubbles: false, solo puede ser escuchado en el mismo elemento donde se despacha. Los elementos ancestros no recibirán el evento. Asegúrate de configurar bubbles: true si necesitas propagación.

Transferir Datos Personalizados

La propiedad detail del custom event te permite transferir cualquier tipo de datos personalizados junto con el evento. Puedes pasar objetos, arrays, funciones o cualquier otro tipo de dato. Los datos son de solo lectura en el listener, lo que previene modificaciones accidentales. Esta es una de las características más poderosas de los custom events.

Transferir Objetos Complejos

La propiedad detail puede contener objetos complejos con múltiples propiedades. Esto te permite transferir información estructurada junto con el evento. Los objetos pueden incluir propiedades como id, nombre, estado, timestamps, o cualquier otra información relevante para tu aplicación.

detail-objeto.js
Loading code...

Este ejemplo muestra cómo transferir un objeto complejo en la propiedad detail. El objeto contiene información sobre un producto agregado al carrito, incluyendo id, nombre, precio, cantidad y timestamp. El listener accede a esta información a través de event.detail para actualizar la interfaz.

Mejor práctica: Estructurar detail con cuidado

Diseña la estructura de detail pensando en la escalabilidad. Usa nombres de propiedades descriptivos y considera versionar tu API de eventos si planeas evolucionar la estructura de los datos en el futuro.

Transferir Arrays y Colecciones

Además de objetos, puedes transferir arrays y colecciones en la propiedad detail. Esto es útil cuando necesitas enviar múltiples elementos o listas de datos junto con el evento. Los arrays pueden contener objetos primitivos u objetos complejos, según tus necesidades.

detail-array.js
Loading code...

Este ejemplo muestra cómo transferir un array de productos en la propiedad detail. El evento 'carrito-actualizado' incluye un array con todos los productos del carrito, permitiendo al listener actualizar la interfaz completa en una sola operación.

Bubbling y Capturing en Custom Events

Los custom events siguen el mismo modelo de propagación que los eventos nativos, soportando bubbling y capturing. En la fase de capturing, el evento desciende desde document hasta el elemento objetivo. En la fase de bubbling, el evento asciende desde el elemento objetivo hasta document. Puedes controlar este comportamiento con las propiedades bubbles y el tercer parámetro de addEventListener().

Fase de Bubbling

Cuando bubbles es true, el evento se propaga hacia arriba en el DOM después de ser despachado en el elemento objetivo. Los listeners en elementos ancestros pueden capturar el evento. Esta es la fase más comúnmente usada y es la que ocurre por defecto en la mayoría de los eventos nativos. Durante el bubbling, los listeners se ejecutan en orden ascendente: primero el elemento objetivo, luego sus ancestros, y finalmente document.

bubbling-custom-event.js
Loading code...

Este ejemplo muestra cómo funciona el bubbling en custom events. El evento se despacha en el botón, pero se captura en el div contenedor y en document. El orden de ejecución es: botón (elemento objetivo) → div (ancestro) → document (raíz). El evento.target siempre apunta al elemento donde se despachó el evento originalmente, no al elemento que lo captura. Esto te permite implementar delegación de eventos y centralizar la lógica de manejo.

Fase de Capturing

La fase de capturing ocurre antes que el bubbling. El evento desciende desde document hasta el elemento objetivo, permitiendo que los listeners en ancestros capturen el evento antes de que llegue al elemento objetivo. Para escuchar en la fase de capturing, pasa true como tercer parámetro a addEventListener(). Esta fase es menos común que bubbling y debe usarse solo cuando realmente necesitas interceptar el evento antes de que llegue al elemento objetivo.

capturing-custom-event.js
Loading code...

Este ejemplo muestra la diferencia entre capturing y bubbling. El listener en document usa true para capturar el evento en la fase de capturing, mientras que el listener en el div captura en la fase de bubbling (default). El orden de ejecución es: document (capturing) → botón (elemento objetivo) → div (bubbling). La mayoría de los casos de uso se resuelven con bubbling.

Errores Comunes

Estos son los errores más frecuentes que encontrarás al trabajar con custom events, especialmente cuando estás aprendiendo o migrando código legacy.

Error: Listener no captura el evento

Un error común es despachar el evento antes de agregar el listener. Como dispatchEvent es síncrono, el listener debe estar registrado antes de despachar el evento, de lo contrario no lo capturará.

error-listener-antes-dispatch.js
Loading code...

El código incorrecto despacha el evento antes de agregar el listener, por lo que el listener nunca se ejecuta. La solución es agregar el listener antes de despachar el evento. Este error es sutil porque no produce un mensaje de error, simplemente el listener no se ejecuta.

Error: Nombres de eventos inconsistentes

Otro error común es usar nombres de eventos inconsistentes entre el despacho y el listener. Los nombres deben coincidir exactamente, incluyendo mayúsculas y minúsculas. Un pequeño error tipográfico puede causar que el listener nunca capture el evento.

error-nombre-evento.js
Loading code...

El código incorrecto usa 'Producto-Agregado' con mayúsculas al despachar, pero 'producto-agregado' con minúsculas al escuchar. Los nombres no coinciden, por lo que el listener nunca se ejecuta. La solución es usar nombres consistentes en ambos lugares.

Error: Mutar event.detail

Aunque es posible mutar event.detail en un listener, esto es una mala práctica porque puede causar efectos secundarios inesperados en otros listeners. Los datos en detail deben tratarse como de solo lectura.

error-detail-mutacion.js
Loading code...

El código incorrecto muta event.detail en el primer listener, lo cual afecta al segundo listener. La solución es crear una copia de los datos si necesitas modificarlos, o evitar la mutación por completo. Esto previene efectos secundarios y hace el código más predecible.

Resumen: Custom Events

Conceptos principales:

  • CustomEvent crea eventos personalizados con datos en detail
  • dispatchEvent dispara el evento en un elemento específico
  • addEventListener escucha custom events como eventos nativos
  • bubbles: true permite propagación a elementos ancestros
  • La propiedad detail transfiere datos personalizados

Mejores prácticas:

  • Usa nombres de eventos con kebab-case (guiones)
  • Agrega listeners antes de despachar eventos
  • Trata event.detail como de solo lectura
  • Usa bubbling para delegación de eventos
  • Estructura detail pensando en escalabilidad