> Manuales > Desarrollo en Javascript del lado del cliente

Explicamos qué son los eventos personalizados, cómo se disparan mediante Javascript y cómo asociar manejadores. Aprenderás cómo los eventos escalan mediante la burbuja de eventos y cómo enviar datos a los manejadores.

Eventos personalizados en Javascript

Los eventos son una de las herramientas más importantes en Javascript para el desarrollo de aplicaciones web. Nos sirven para responder a la interacción del usuario, para posibilitar la interoperabilidad entre plugins o componentes, así como para reaccionar ante cualquier suceso que ocurra en general. Seguro que has trabajado con eventos en Javascript y sabes de lo que estamos hablando, pero ¿Qué sabes de los eventos personalizados?

Los eventos personalizados son una de las herramientas que tenemos para conseguir que los componentes, piezas, plugins o como los quieras llamar se comuniquen entre sí y entre otros elementos de la página. Se usan mucho en el desarrollo de Javascript, por lo que te resultarán muy útiles. En este artículo te explicaremos todo lo que necesitas saber sobre los eventos personalizados, es decir, cómo dispararlos, cómo capturarlos y ejecutar manejadores de eventos, cómo enviar datos junto con los eventos y mucho más.

Para qué sirven los eventos personalizados

La primera vez que oí hablar de los eventos personalizados no entendí muy bien para qué los queríamos. Supongo que podrá pasar parecido con algunos lectores de este artículo.

Los eventos personalizados los entendemos en el contexto de los componentes, por llamar así a cualquier pieza de software que se ejecuta en el navegador. Estos componentes son generalmente piezas que son capaces de encapsular una funcionalidad y permitir su reutilización a lo largo de un proyecto o varios proyectos.

Dependiendo de tus costumbres o incluso de las tecnologías o librerías con las que trabajes podrás usar el nombre de "componentes", pero puede que los llames "plugins", "custom elements" de web components o simplemente elementos del DOM que hayas extendido por cualquier medio.

Los componentes generalmente tienen una funcionalidad y cuando pasan cosas con ellos a veces tienen que avisar a otros elementos de su alrededor para informarles que en ellos ha ocurrido algo. Para conseguir eso, en Javascript en el contexto del navegador, levantamos eventos.

Los eventos son señales que nos permiten saber que han pasado cosas. Y "cosas" puede ser algo de lo más amplio. Hay "cosas estándar", como por ejemplo que se haya hecho clic encima de un botón o enlace. Esas "cosas estándar" ya vienen definidas con eventos existentes en Javascript, como "click", pero también hay cosas que pueden ocurrir y que son totalmente arbitrarias, como por ejemplo que hayan pasado 3 minutos desde que se entró en una página web, que se hayan eliminado todos los elementos de una lista, o que se haya seleccionado una fecha del mes siguiente en un calendario.

Esas cosas, por supuesto, no tienen eventos estándar del navegador, pues sería imposible tener eventos ya predefinidos para millones de situaciones que pueden ocurrir en una aplicación web. Para ellas usamos eventos personalizados.

Los eventos personalizados pueden tener cualquier nombre que deseemos nosotros y sirven para informar al exterior de cosas que han ocurrido en los componentes. Espero que más o menos el concepto lo podamos entender.

Por ahondar en el ejemplo del componente de calendario, lo lógico es que el desarrollo sea capaz de adaptarse a la mayor cantidad de situaciones. Por ello sería ideal que el componente avisase de una serie de cosas que puedan ocurrir con él, para que todas las personas que implementen ese calendario lo puedan usar en la mayor cantidad de situaciones. Por ejemplo, sería lógico que este componente avisase con un evento cuando se ha seleccionado una fecha. Avisase si se ha editado a mano la fecha y el formato no es correcto y cosas similares. De este modo, cualquier persona será capaz de usar ese calendario y personalizar comportamientos ya específicos de la aplicación concreta donde se usa el calendario. Por ejemplo podríamos capturar el evento personalizado de cambio de fecha y realizar acciones cuando han seleccionado una fecha del pasado, para informar al usuario que esa fecha no es posible usarla. Vale cualquier ejemplo, ya que podríamos pensar en miles de situaciones en las que diversas partes de la página deben estar atentas a cosas que ocurran en los componentes que estamos creando.

Cómo se dispara un evento personalizado en Javascript

Para disparar eventos personalizados en Javascript nativo, es decir, el Javascript que entiende el navegador, todos los elementos del DOM tienen un método llamado dispatchEvent(). Este es el método que usaremos para levantar nuestros propios eventos personalizados, el cual necesitará que le informemos de los siguientes parámetros:

El código para lanzar un evento personalizado sería más o menos así

// Accedo a un elemento del DOM
var elemento = document.getElementById('elemento');
// Disparo el evento personalizado
this.dispatchEvent(new CustomEvent('nombre-del-evento'));

Cómo se asocia un manejador de evento personalizado

Los eventos no tendrían mucho sentido si no podemos asociar manejadores de eventos cuando ejecutar acciones cuando éstos se disparan. Los eventos personalizados los capturamos y asociamos manejadores igual que lo hacemos con los eventos normales.

Para ello usamos el método addEventListener(), indicando el nombre del evento personalizado que queremos usar.

// Traigo un elemento del DOM
var elemento = document.getElementById('elemento');
// Le asocio un manjeador de evento
lista.addEventListener('nombre-del-evento', function() {
      console.log('detectado evento nombre-del-evento');
});

Como puedes ver, los manejadores de eventos se asocian a los elementos del DOM. Dicho de otra manera, los eventos se escuchan sobre los elementos del DOM donde queramos reaccionar a ellos. Este evento lo estamos escuchando sobre el elemento con id="elemento".

Para entender este punto y la importancia que tiene en la gestión de eventos es importante hablar de la burbuja de eventos del navegador.

Burbuja de eventos Javascript

Ten en cuenta que los elementos de la página forman una jerarquía. Esta jerarquía se refiere a la estructura de las etiquetas que hay en el documento HTML y está modelada en lo que llamamos el DOM (Document Object Model).

Puedes ver el artículo Jerarquía de objetos del navegador para entender mejor este concepto.

La burbuja de eventos del navegador hace que los eventos suban por la jerarquía de objetos, paso a paso, hasta llegar al objeto principal de esta jerarquía, que es el objeto window del navegador.

Esto quiere decir que los eventos irán subiendo, comenzando en el elemento que ha lanzado el evento, subiendo primero a su padre y luego al elemento abuelo, llegando hasta el objeto document y por último al objeto window

Vamos a explicarlo con un ejemplo para que se pueda entender perfectamente. Pero primero veamos la siguiente imagen:

Eventos personalizados en Javascript

Un evento se puede originar desde cualquier elemento del DOM. Supongamos que del span, que está debajo del todo (aunque podría haber sido en el párrafo, etiqueta "p", o en el header, por ejemplo). Entonces, dada la burbuja y en el caso que el evento se origine sobre el span, este evento lo podríamos escuchar en el propio span, en el elemento li padre, en el elemento ul, el main, body, document y por último en window.

Esto quiere decir que en la práctica podríamos escuchar un evento personalizado originado en el span en todos los elementos de la jerarquía por los que vamos pasando hasta que llegamos al objeto window.

Pero ojo!! esto no tiene por qué ser siempre así, pues para que el evento personalizado escale mediante la burbuja, tenemos que configurarlo correctamente.

Propiedad bubbles de los eventos personalizados

Los eventos personalizados por defecto no escalan por la burbuja de eventos. Esto quiere decir que solamente los podríamos escuchar con un manejador de eventos que se haya asociado sobre el mismo elemento donde se originó.

Para indicar que el evento personalizado debe subir por la burbuja debemos indicarlo en el objeto de configuración de creación del evento. En ese objeto usaremos una propiedad llamada "bubbles", que debemos asignar a true.

elemento.dispatchEvent(new CustomEvent('nombre-del-evento', {
    bubbles: true
}));

Con esta sencilla configuración nos aseguramos que el evento subirá hasta llegar a window. Es decir, la burbuja activada hará el trabajo de subir el evento hasta el inicio de la jerarquía.

Por tanto, gracias a la burbuja, si es que está configurada en el evento personalizado, podríamos perfectamente crear el manejador en el objeto document, o en el window, ya que el evento siempre llegará a propagarse hasta llegar a ellos.

document.addEventListener('nombre-del-evento', function(e) {
    console.log('El evento también lo puedo capturar en el objeto document, si se activó bubbles');
});

Propiedad composed de los eventos personalizados

No me quería olvidar de mencionar la propiedad "composed", que no es tan conocida y ni siquiera la vamos a necesitar siempre, pero sirve para asegurarnos de que el evento suba desde el shadow DOM de un hipotético custom element hacia el DOM común.

Si no trabajas con el estándar de Web Components no te hará mucha falta esta propiedad, aún así este sería un ejemplo:

elemento.dispatchEvent(new CustomEvent('nombre-del-evento', {
    bubbles: true,
    composed: true
}));

Cómo pasar datos en los eventos personalizados

También es muy interesante la posibilidad de enviar datos junto con el evento. Con ello podemos conseguir informar de cualquier cosa que se necesite al elemento donde estemos escuchando los eventos personalizados.

Para que se entienda por qué queremos enviar datos vamos a poner un par de ejemplos. Vamos a suponer que tenemos elementos de una lista cada elemento de esta lista tiene un botón para eliminar ese ítem. Cuando se pulsa el botón de borrar se puede informar de qué elemento se había intentado borrar, para que la persona que escuche el evento sepa reaccionar correctamente. Ahora por ejemplo piensa en un reproductor de vídeo. Cuando se hace stop sobre el reproductor puede enviar un evento para avisar que se ha parado la reproducción, en cuyo caso sería interesante enviar un dato sobre el segundo en el que el vídeo se ha detenido. Con nuestro componente de fecha, cuando se seleccione una nueva fecha levantaremos un evento personalizado que indicará la fecha que se ha colocado. Puedes encontrar miles de casos en los que puede ser interesante enviar datos junto con el evento.

Enviar los datos al disparar el evento

Para enviar datos en los eventos personalizados usaremos también el objeto de configuración del evento, mediante una propiedad llamada "detail". Veamos este ejemplo de código.

elemento.dispatchEvent(new CustomEvent('nombre-del-evento', {
    bubbles: true,
    composed: true,
    detail: {
        saludo: 'hola',
        repeticiones: 3
    }
}));

En la propiedad detail he colocado un objeto, que tiene dos propiedades con dos datos. No estoy obligado a que el detail sea siempre un objeto, también podría ser un dato simple, como un número o una cadena.

elemento.dispatchEvent(new CustomEvent('nombre-del-evento', {
    bubbles: true,
    composed: true,
    detail: 'id3'
}));

Sin embargo, como buena práctica recomendaría siempre enviar objetos en el detail, aunque solamente necesites informar de un único dato, más que nada para conseguir dos beneficios:

Recibir los datos en el manejador del evento

Ahora vamos a ver cómo tendríamos que recibir los datos enviados mediante el evento. Para ello usamos el objeto evento que se recibe en cualquier función que hace las veces de manejador del evento.

Ese objeto evento es muy útil, ya que no solo nos entrega datos sobre el evento que se ha producido, sino que también aporta cierta funcionalidad sobre el evento capturado. Si no necesitamos el objeto evento para nada no es obligatorio recibirlo como parámetro. Se habla más sobre esto en el artículo de los eventos en Javascript.

Con el siguiente código vemos cómo asociar un manejador de evento y cómo recibir el objeto evento. Dentro de ese objeto disponemos de la propiedad "detail", sobre la que podemos acceder a los datos que nos ha enviado el evento, si es que se envió algo.

lista.addEventListener('nombre-del-evento', function(e) {
    console.log(`El saludo recibido es ${e.detail.saludo} con ${e.detail.repeticiones} repeticiones`);
});

Conclusión

Hemos visto qué son los eventos personalizados en Javascript y para qué los podríamos usar en el contexto de ejecución del navegador. Hemos visto con ejemplos cómo podemos disparar eventos personalizados y cómo podemos capturarlos y ejecutar manejadores de eventos.

La práctica del trabajo con eventos es muy habitual cuando se desarrollan componentes y es básica para que los componentes puedan interactuar con otros componentes o la página pueda reaccionar al uso que se haga de esos componentes. Esperamos que los conocimientos hayan quedado claros y que puedas usarlos en tu día a día del desarrollo frontend.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual