Why should you use custom events in JavaScript?

JavaScript: Crea tus Propios Eventos Personalizados

15/12/2019

Valoración: 4.3 (9000 votos)

En el mundo del desarrollo web con JavaScript, estamos acostumbrados a trabajar con eventos. El clic de un ratón, el envío de un formulario o la carga completa de una página son señales que el navegador nos envía para que podamos reaccionar y ejecutar nuestro código. Sin embargo, a medida que nuestras aplicaciones crecen en complejidad, los eventos nativos del DOM (Document Object Model) pueden quedarse cortos. ¿Qué sucede cuando necesitamos que diferentes partes de nuestra aplicación se comuniquen entre sí sin estar directamente conectadas? Aquí es donde entran en juego los eventos personalizados, una herramienta increíblemente poderosa que te permite crear tu propio sistema de comunicación interno, haciendo tu código más limpio, modular y fácil de mantener.

How do I listen to a custom event in JavaScript?
To listen for the custom event, add an event listener to the element you want to listen on, just as you would with native DOM events. console.log("I'm listening on a custom event"); }); How do JavaScript custom events work?
Índice de Contenido

¿Por qué los Eventos Nativos No Siempre Son Suficientes?

Los eventos estándar como click, submit o keydown son fantásticos para responder a las interacciones del usuario con la interfaz. Pero su alcance está limitado al DOM. Piensa en situaciones puramente lógicas de tu aplicación:

  • Un usuario inicia sesión y varias partes de la UI necesitan actualizarse (el avatar en la cabecera, el mensaje de bienvenida, el menú de opciones).
  • Los datos de un producto se cargan desde una API y un carrito de compras necesita saber si el stock ha cambiado.
  • Un flujo de varios pasos es completado por el usuario y necesitas activar una notificación de éxito.

En estos casos, no hay un "evento" de DOM que represente estas acciones. Podríamos solucionarlo con callbacks o acoplando directamente los componentes, pero esto a menudo conduce a un código enrevesado y frágil, conocido como "código espagueti". La solución más elegante es crear nuestros propios eventos que representen estas acciones específicas de la aplicación.

Creando y Despachando Eventos con la API Nativa

JavaScript nos proporciona una forma estándar y muy sencilla de crear nuestros propios eventos a través del constructor CustomEvent. Este método es ideal cuando la comunicación que necesitas está ligada a elementos del DOM.

Paso 1: Crear el Evento

Para crear un evento, utilizamos el constructor new CustomEvent(). Este recibe dos argumentos: el nombre del evento (una cadena de texto que tú inventas) y un objeto de opciones.

// Creamos un evento llamado 'profileUpdated' const profileUpdateEvent = new CustomEvent('profileUpdated', { detail: { username: 'JuanDev', level: 15 } });

La propiedad más importante en el objeto de opciones es detail. Este es un objeto donde puedes colocar cualquier dato que quieras enviar junto con el evento. Es la forma de pasar información desde el punto donde se origina el evento hasta el punto donde se escucha.

Why should you use custom events in JavaScript?
Improved Readability: Using custom events can make your code more readable and intuitive. By naming events descriptively, you can make the intent of your interactions clearer and easier to understand. To create custom events in JavaScript, you use the CustomEvent constructor.

Paso 2: Despachar (Disparar) el Evento

Una vez creado el evento, necesita ser "disparado" desde un elemento del DOM. Para ello, utilizamos el método dispatchEvent() en el elemento que queramos que sea el emisor.

// Seleccionamos el contenedor del perfil del usuario const userProfileElement = document.querySelector('#user-profile'); // Disparamos el evento desde ese elemento userProfileElement.dispatchEvent(profileUpdateEvent);

Ahora, el evento profileUpdated ha sido emitido desde el elemento #user-profile.

Paso 3: Escuchar el Evento

Finalmente, cualquier otra parte de tu código puede escuchar este evento utilizando el conocido método addEventListener(), exactamente igual que lo harías con un evento de clic.

// El componente de la cabecera escucha el evento const headerElement = document.querySelector('header'); headerElement.addEventListener('profileUpdated', (event) => { console.log('¡El perfil se ha actualizado!'); // Accedemos a los datos enviados a través de event.detail const { username, level } = event.detail; // Actualizamos la UI de la cabecera document.querySelector('#header-username').textContent = `Hola, ${username} (Nivel ${level})`; });

¡Y listo! El componente de la cabecera se ha actualizado sin tener conocimiento directo de la lógica que actualizó el perfil. Esto es desacoplamiento en acción.

How to create a custom event?
To create a custom event, you use the CustomEvent() constructor: The CustomEvent() has two parameters: The eventType is a string that represents the name of the event. The options is an object has the detail property that contains any custom information about the event. The following example shows how to create a new custom event called highlight:

Opciones Avanzadas del Constructor

El objeto de opciones de CustomEvent permite configurar el comportamiento del evento:

  • bubbles: Un booleano (false por defecto) que, si se establece en true, hace que el evento se propague hacia arriba en el árbol del DOM, desde el elemento origen hacia sus padres, abuelos, etc.
  • cancelable: Un booleano (false por defecto) que, si se establece en true, permite que el evento sea cancelado usando event.preventDefault().
  • composed: Un booleano (false por defecto) que permite que el evento atraviese los límites del Shadow DOM, algo crucial cuando se trabaja con Web Components.

Construyendo tu Propio Sistema de Eventos (Patrón Pub/Sub)

A veces, nuestros eventos no están relacionados con ningún elemento del DOM. Son eventos puramente lógicos que ocurren en el backend de nuestra aplicación de navegador. Para estos casos, construir un sistema de eventos simple, a menudo llamado emisor de eventos (Event Emitter) o patrón Publicador/Suscriptor (Pub/Sub), es una excelente alternativa.

La idea es tener un objeto central que gestione los eventos. Los componentes pueden "suscribirse" a un evento y el código que lo genera puede "publicarlo" a través de este objeto central.

Implementación de un `EventEmitter` Básico

Podemos crear una clase simple en JavaScript para manejar esta lógica.

class EventEmitter { constructor() { this.listeners = {}; } // Suscribirse a un evento on(event, listener) { if (!this.listeners[event]) { this.listeners[event] = []; } this.listeners[event].push(listener); } // Darse de baja de un evento off(event, listener) { if (!this.listeners[event]) return; this.listeners[event] = this.listeners[event].filter( (l) => l !== listener ); } // Emitir o publicar un evento emit(event, ...args) { if (!this.listeners[event]) return; this.listeners[event].forEach((listener) => { listener(...args); }); } }

Uso del `EventEmitter`

Ahora, podemos crear una instancia de nuestro emisor y usarla en toda la aplicación para comunicarnos.

What is a custom JavaScript event system?
Creating a custom event system for your web apps can do a great deal to enhance the way you handle interactions. Creating a Custom JavaScript Event System provides you with total control over how events fire, listen, and are acted upon.
// Creamos una instancia global para nuestra app const appEvents = new EventEmitter(); // --- En el Módulo de Notificaciones --- function mostrarNotificacion(mensaje, tipo) { console.log(`NOTIFICACIÓN [${tipo}]: ${mensaje}`); } appEvents.on('mostrar-notificacion', mostrarNotificacion); // --- En el Módulo de Autenticación --- function iniciarSesion(usuario, password) { // ... lógica de inicio de sesión if (loginExitoso) { // Emitimos el evento con datos appEvents.emit('mostrar-notificacion', 'Inicio de sesión correcto', 'exito'); } else { appEvents.emit('mostrar-notificacion', 'Credenciales incorrectas', 'error'); } } iniciarSesion('usuario', 'pass123'); // Esto disparará la notificación

Como puedes ver, el módulo de autenticación no sabe nada sobre el módulo de notificaciones. Simplemente emite una señal al sistema central, y cualquier módulo suscrito a esa señal reaccionará. Esto es ideal para una modularidad y escalabilidad máximas.

Tabla Comparativa: API Nativa vs. Sistema Propio

¿Cuándo deberías usar uno u otro? Aquí tienes una comparación para ayudarte a decidir.

CaracterísticaAPI Nativa (CustomEvent)Sistema Propio (EventEmitter)
DependenciaRequiere un elemento del DOM para despachar y escuchar.Independiente del DOM. Basado en un objeto JavaScript.
Caso de Uso PrincipalComunicación entre componentes de la UI, Web Components.Comunicación de lógica de aplicación, gestión de estado.
Paso de DatosA través de la propiedad event.detail.Como argumentos adicionales en el método emit().
ComplejidadMuy bajo. Ya está integrado en el navegador.Baja. Requiere implementar o importar una pequeña clase.

Preguntas Frecuentes (FAQs)

¿Por qué no usar siempre addEventListener para todo?
addEventListener es para escuchar eventos, no para crearlos. Es perfecto para eventos de DOM nativos, pero para la comunicación entre módulos de tu propia lógica, necesitas algo que emita esas señales. Los eventos personalizados son precisamente esas señales que luego puedes escuchar con addEventListener (si usas la API nativa).
¿Puedo usar un sistema de eventos personalizado en una aplicación grande como las construidas con React o Vue?
¡Absolutamente! De hecho, los frameworks modernos usan conceptos similares internamente. Vue tiene un sistema de eventos con $emit y $on. En React, aunque el flujo de datos es más unidireccional, se pueden usar librerías externas de gestión de estado o emisores de eventos para comunicar componentes no relacionados directamente.
¿Cómo puedo priorizar los "listeners" o escuchas?
En nuestra implementación básica de EventEmitter, los listeners se ejecutan en el orden en que se registraron. Si necesitaras prioridades, podrías modificar el método on() para aceptar un argumento de prioridad y almacenar los listeners en un array ordenado por ese valor antes de ejecutarlos en el método emit().
¿Cuál es la ventaja de un listener de un solo uso?
A veces, solo necesitas reaccionar a un evento una única vez, por ejemplo, para una inicialización. Un método once() (que puedes añadir a la clase EventEmitter) registraría un listener que se elimina automáticamente después de su primera ejecución, evitando fugas de memoria y ejecuciones no deseadas.

Conclusión

Dominar los eventos personalizados en JavaScript, ya sea a través de la API nativa CustomEvent o construyendo tu propio EventEmitter, es un paso fundamental para escribir código más profesional, desacoplado y mantenible. Te permiten crear un sistema de comunicación robusto donde las diferentes partes de tu aplicación pueden hablar entre sí sin crear una red de dependencias frágiles. La próxima vez que te encuentres pasando callbacks a través de múltiples niveles de componentes, detente y pregúntate: ¿podría un evento personalizado simplificar esto? La respuesta, muy probablemente, será que sí.

Si quieres conocer otros artículos parecidos a JavaScript: Crea tus Propios Eventos Personalizados puedes visitar la categoría Juegos.

Subir