← El DOM: la web que responde

Sesión 5 · Semana 2

El objeto evento y la delegación

Hoy · Hoja de ruta

  1. 1. Aprende: Qué información trae el evento, cómo se propaga y qué es delegar.
  2. 2. Haz: Un solo manejador que atienda a todas las tarjetas del catálogo.
  3. 3. Comprueba: Funciona también con las tarjetas que aún no existen.

Antes de empezar · 5 minutos, sin apuntes

  1. Si tienes cincuenta botones iguales, ¿registrarías cincuenta escuchas?
  2. ¿Cómo sabrías cuál de ellos se ha pulsado?
  3. ¿Qué pasa con los botones que se crean después?

El objeto que recibe el manejador

catalogo.addEventListener("click", (evento) => {
  evento.target;            // el elemento exacto donde se pulsó
  evento.currentTarget;     // el elemento que escucha
  evento.type;              // "click"
  evento.key;               // en eventos de teclado
  evento.preventDefault();
  evento.stopPropagation();
});

La pareja target / currentTarget es la clave de esta sesión: puedes escuchar en el contenedor y averiguar dentro cuál de sus hijos recibió la acción.

La propagación

Un clic no ocurre solo en un elemento: recorre el árbol.

El camino de un evento
  1. Captura · del documento hacia abajo
  2. Objetivo · el elemento pulsado
  3. Burbujeo · hacia arriba, hasta el documento

El burbujeo es lo que hace posible la delegación y explica además un fallo característico: pulsar en un botón dentro de una tarjeta que también escucha el clic dispara los dos manejadores.

stopPropagation es el último recurso

Detener la propagación arregla el síntoma y deja una trampa: cualquier manejador que alguien registre más arriba —cerrar un menú, un contador de uso— dejará de enterarse, y quien lo escriba no entenderá por qué.

Antes de detenerla, comprueba si el manejador de arriba puede filtrar por target. Casi siempre puede.

Delegar

catalogo.addEventListener("click", (evento) => {
  const boton = evento.target.closest("[data-accion='añadir']");
  if (!boton) return;                     // el clic no era en un botón nuestro

  const id = Number(boton.dataset.id);
  añadirAlCarrito(id);
});

Tres ventajas, y son grandes: una sola escucha en lugar de cincuenta; funciona con las tarjetas que tu código creará después; y no hay que registrar nada de nuevo tras volver a pintar.

closest

Sube por los antepasados desde el elemento pulsado hasta encontrar uno que case con el selector. Resuelve el problema de que el clic caiga en un icono o en el texto de dentro del botón, y no en el botón mismo.

Teclado, siempre

buscador.addEventListener("keydown", (evento) => {
  if (evento.key === "Escape") limpiarBusqueda();
});

Si delegas clics en elementos que son botones de verdad, el teclado ya funciona: pulsar Intro sobre un botón genera un click. Es otra razón para usar el elemento correcto en lugar de un div decorado.

Tarea 5 · Un manejador para todos

  1. Añade a cada tarjeta de tu catálogo un botón con data-accion y data-id.
  2. Registra una sola escucha en el contenedor.
  3. Usa closest para localizar el botón y dataset para el identificador.
  4. Añade una segunda acción y distíngula por su data-accion.
  5. Comprueba con el teclado que ambas funcionan.
  6. Registra una escucha en el contenedor y otra en la tarjeta, y observa el orden.
Objetivo mínimoUna escucha delegada que distingue dos acciones y el producto afectado.
Si lo tienesAñade Escape para limpiar la búsqueda y Intro para aplicar el filtro.
RetoExplica un caso real en el que stopPropagation rompería otra funcionalidad.

Checkpoint · fin de la sesión 5

  • Distingues target de currentTarget.
  • Explicas el burbujeo con un ejemplo de tu página.
  • Delegas con closest y sales pronto si no procede.
  • Todo lo que funciona con ratón funciona con teclado.

Antes de cerrar · 2 minutos, sin mirar

  1. ¿Qué ventaja tiene delegar?
  2. ¿Para qué sirve closest?
  3. ¿Por qué evitamos stopPropagation?
Ver respuestas

1 · Una sola escucha, y funciona con los elementos creados después.

2 · Para subir desde el elemento pulsado hasta el contenedor que te interesa, aunque el clic cayera en un icono interior.

3 · Porque impide que otros manejadores más arriba se enteren del evento, y eso rompe cosas a distancia.