No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Delegación de eventos y prevención de comportamiento predeterminado

19/27
Recursos

¿Qué es el Delegation Pattern y cómo se utiliza en eventos de clic?

El Delegation Pattern, o patrón de delegación, es una técnica utilizada en JavaScript para optimizar la gestión de eventos. Este patrón es especialmente útil cuando se trabaja con elementos dinámicos en el DOM (Document Object Model). En lugar de asignar un evento a cada elemento individual, se delega la escucha del evento a un elemento padre. Así, el elemento padre puede reconocer y gestionar los eventos de sus hijos.

A continuación, te explicamos cómo aplicar este patrón para cambiar el estilo de los elementos de una lista al hacer clic.

¿Cómo cambiar el color de fondo de una lista con eventos de JavaScript?

Vamos a explorar dos métodos para cambiar el color de fondo de elementos en una lista HTML cuando se les hace clic. El objetivo es aplicar una clase que modifique el estilo al elemento seleccionado.

Método 1: Asignar eventos individualmente a cada ítem

Una forma simple pero menos eficiente es agregar un EventListener a cada elemento de la lista. Aquí está cómo se hace en JavaScript:

const listItems = document.querySelectorAll("li");

listItems.forEach(item => {
    item.addEventListener("click", event => {
        const target = event.target;
        target.classList.toggle("highlight");
    });
});

En este código:

  • Se seleccionan todas las etiquetas <li> del documento y se almacenan en una variable listItems.
  • Se utiliza forEach para agregar un evento click a cada elemento.
  • Se alterna la clase highlight al hacer clic, lo que cambia el estilo del elemento.

Aunque este método es directo, puede afectar el rendimiento si se tiene una lista con muchos elementos.

Método 2: Usar el Delegation Pattern para gestionar eventos

Para una mejor práctica, podemos utilizar el Delegation Pattern, asignándole el evento al elemento padre (<ul>) y haciendo que este maneje la interacción con sus hijos:

const list = document.querySelector("ul");

list.addEventListener("click", event => {
    const li = event.target.closest("li");
    if (li) {
        li.classList.toggle("highlight");
    }
});

En este enfoque:

  • Se selecciona la lista completa (<ul>) y se le asigna un EventListener para el evento click.
  • Utilizamos event.target.closest("li") para obtener el elemento <li> más cercano dentro de la lista. Esto asegura que, independientemente de dónde se haga clic dentro de un ítem, será el elemento <li> el que cambie de clase.

Este método es más eficiente porque reduce el número de listeners activos en el documento, delegando la responsabilidad al elemento padre.

¿Por qué elegir el Delegation Pattern?

El Delegation Pattern mejora el rendimiento de la aplicación al:

  • Reducir la cantidad de eventos activos a gestionar.
  • Simplificar el código y mejorar su mantenibilidad.
  • Resolver problemas de selección en estructuras HTML más complejas.

En resumen, el empleo de esta técnica no solo optimiza el funcionamiento de tu aplicación, sino que también permite manejar elementos dinámicos de forma más efectiva. Convierte esta práctica en una parte fundamental de tu flujo de trabajo en JavaScript. ¡Sigue mejorando y explorando las ventajas de los patrones de diseño en el desarrollo web!

Aportes 18

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Aqui les dejo mi aporte: ```js const listParent = document.querySelector('ul') //se invoca al elemento declarado anteriormente listParent.addEventListener('click',(e)=>{ //esta variable "listItem" ayuda a guardar el método closest() const listItem = e.target.closest('li') //luego validamos si el evento se dispara, entoces que agrege la clase "highlight" if (listItem) { listItem.classList.toggle('highlight') } }) ```
Por ejmplo, algo que vi en un comentario de esta clase y me parece importante es que validemos que se seleccione el elmento correcto, por que si no lo hacemos saldra un error en consola como le paso al profe en el minuto 8:19, les dejo como podrian solucionarlo, igualmente no afecta en nada que salga eso en la consola o eso creo yo, pero a mi me molesta. ![](https://static.platzi.com/media/user_upload/image-e106b528-bbff-470b-ae59-ba1821d497f8.jpg)
El HTML de la clase: ```html <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> main { display: flex; justify-content: center; align-items: center; height: 100vh; } ul { list-style: none; padding: 0; margin: 0; } li { padding: 1rem; border: 1px solid black; margin: 1rem; cursor: pointer; } li:hover { background-color: #ccc; } .highlight { background-color: #81fa07; color: white; } </style> </head> <body> <main>
  • Option 1
  • Option 2
  • Option 3
  • Option 4
  • Option 5
</main> <script src="19app.js"></script> </body> </html> ```\\<html lang="en"> \<head>  \<meta charset="UTF-8">  \<meta name="viewport" content="width=device-width, initial-scale=1.0">  \<title>Document\</title>   \<style>    main {      display: flex;      justify-content: center;      align-items: center;      height: 100vh;    }     ul {      list-style: none;      padding: 0;      margin: 0;    }     li {      padding: 1rem;      border: 1px solid black;      margin: 1rem;      cursor: pointer;    }     li:hover {      background-color: #ccc;    }     .highlight {      background-color: #81fa07;      color: white;    }  \</style>\</head> \<body>  \<main>    \
          \
  • Option 1\
  •       \
  • Option 2\
  •       \
  • Option 3\
  •       \
  • Option 4\
  •       \
  • Option 5\
  •     \
  \</main>  \<script src="19app.js">\</script>\</body> \</html>
Dejo por aquí el código del profesor para poder echarle un vistazo con más calma: ```js //añadiendo el mismo listener a cada elemento const listItems = document.querySelectorAll("li") listItems.forEach( (item) => { item.addEventListener('click', (event) => { event.target.classList.toggle('highlight') }) }) //añadiendo un solo listener al padre const list = document.querySelector('ul') list.addEventListener('click', (event) => { event.target.closest('li').classList.toggle('highlight') }) ```
* **Problema de rendimiento:** Crear un nuevo manejador de eventos para cada elemento puede afectar el rendimiento cuando hay muchos elementos en el DOM * **Delegación de eventos:** Consiste en agregar un único escuchador de eventos a un elemento padre común. Esto mejora el rendimiento y es más fácil de mantener.
viendo este ejercicio le pregunte a chatgpt que pasaba si se hace click dentro de la ***ul******* pero fuera de un ***li*** esto explica: Si haces clic dentro de la `
    ` pero **fuera de un** `
  • `, el código puede fallar porque la llamada a `event.target.closest("li")` devolverá `null`. Esto sucede porque no hay un `
  • ` en la jerarquía de elementos donde ocurrió el clic. ### **Problema específico:** Cuando haces clic fuera de un `
  • `, el método `closest("li")` devuelve `null`. Luego, al intentar acceder a `.classList` de un valor `null`, el navegador lanzará un error como: Uncaught TypeError: Cannot read properties of null (reading 'classList') Si haces clic dentro de la `
      ` pero **fuera de un** `
    • `, el código puede fallar porque la llamada a `event.target.closest("li")` devolverá `null`. Esto sucede porque no hay un `
    • ` en la jerarquía de elementos donde ocurrió el clic. ### **Problema específico:** Cuando haces clic fuera de un `
    • `, el método `closest("li")` devuelve `null`. Luego, al intentar acceder a `.classList` de un valor `null`, el navegador lanzará un error como: javascriptCopyEdit`Uncaught TypeError: Cannot read properties of null (reading 'classList'`) ### **Solución: Verificar si** `closest("li")` **devuelve un elemento válido** Podemos agregar una validación para asegurarnos de que el clic ocurrió en un `
    • ` o dentro de un `
    • ` antes de intentar manipularlo: ![](https://static.platzi.com/media/user_upload/image-a6d7b5de-9db8-465c-8653-fe264e539c07.jpg)![](https://static.platzi.com/media/user_upload/image-44c17f64-f41a-4bef-b278-17d08be045fa.jpg)
Igualmente no estaría de más, por si acaso, verificar si exactamente lo que estamos haciendo clic es uno de los elementos li que nos interesa (y que no sea otro li diferente quizás fuera de la lista ul), ya que al dejarlo simplemente con un "closest" de esa forma si se hace clic en los espacios vacíos entre los li (o sea, haciendo clic dentro de la lista ul pero sin seleccionar ninguno de los li) dependiendo de cómo tengamos estructurado nuestro HTML es posible que otro li (que no sea ninguna de las opciones dentro del ul) sea el que se termine iluminando. Aunque en este caso específico de la clase todo funciona perfectamente ya que los únicos li son los que están dentro del ul. Pero en caso de que se vaya a usar en algún proyecto, estaría bien escribir dentro del closest() los li con una clase que nos asegure que solo son ellos y no otros li que estén por nuestro HTML.
¿Alguien más nota un delay al dispapar el evento? Además cuando hago click sobre el texto no se dispara con la segunda forma
**Concepto de delegación de eventos** La delegación de eventos es un patrón de programación que permite manejar eventos en JavaScript de manera más eficiente y escalable. Funciona aprovechando la propagación de eventos (event bubbling) en el DOM **Cómo funciona la delegación de eventos** La delegación de eventos se basa en la idea de delegar el manejo de un evento a un elemento superior en la estructura DOM, en lugar de al elemento que recibió el evento originalmente [**1**](https://www.freecodecamp.org/news/event-delegation-javascript/). **Beneficios de la delegación de eventos** 1. Eficiencia en memoria: Reduce el número de oyentes de eventos, lo que puede ahorrar memoria y mejorar el rendimiento, especialmente con muchos elementos. 2. Manejo de elementos dinámicos: Permite manejar eventos en elementos añadidos dinámicamente al DOM después del cargado inicial de la página. 3. Simplicidad: Simplifica la gestión de oyentes de eventos, especialmente cuando muchos elementos comportan de manera similar.
Mi forma de hacerlo: ```js const list = document.querySelector("ul"); list.addEventListener("click", (event) => { // Verifica si el elemento clickeado es un
  • if (event.target.tagName === "LI") { // Cambia el color de fondo del elemento actual event.target.classList.toggle("highlight"); // Cambia el color de fondo de los demás elementos a su color original const listItems = list.querySelectorAll("li"); listItems.forEach((item) => { if (item !== event.target) { item.classList.remove("highlight"); } }); } }); ```const list = document.querySelector("ul"); list.addEventListener("click", (*event*) => {    // Verifica si el elemento clickeado es un \
  •     if (event.target.tagName === "LI") {        // Cambia el color de fondo del elemento actual         event.target.classList.toggle("highlight");         // Cambia el color de fondo de los demás elementos a su color original                const listItems = list.querySelectorAll("li");         listItems.forEach((*item*) => {            if (item !== event.target) {                item.classList.remove("highlight");            }        });    }});
  • \
    \<button class="btn">Botón 1\</button> \<button class="btn">Botón 2\</button> \<button class="btn">Botón 3\</button> \
    \<script> document.getElementById("contenedor").addEventListener("click", function(event) { if (event.target.classList.contains("btn")) { alert("Clic en: " + event.target.textContent); } }); \</script> ¿Qué pasa aquí? ✅ Solo se asigna un solo evento al #contenedor. ✅ Se usa event.target para verificar si el clic fue en un botón. ✅ Si se agregan más botones dinámicamente, el evento sigue funcionando.
    El concepto a través de una analogía: En lugar de darle instrucciones individualmente a cada hijo (cada elemento), es más eficiente decirle al padre (el contenedor) que esté atento a sus hijos y actúe cuando corresponda. Es como si en una **fiesta** en lugar de avisar a ***cada*** niño que es hora de cenar, le dijeras al anfitrión (el padre) que avise cuando sea necesario.
    ![](https://static.platzi.com/media/user_upload/Captura%20de%20pantalla%202025-01-19%20141356-59875a2f-f3eb-41b2-b680-283292e47c0d.jpg)
    En la clase sobre "Delegación de eventos y prevención de comportamiento predeterminado", se presentaron los siguientes conceptos: 1. **Delegación de Eventos**: Consiste en asignar un solo event listener a un elemento padre en lugar de múltiples listeners a los hijos, mejorando el rendimiento. 2. **Burbuja de Eventos**: Al hacer clic en un elemento, el evento sube por la jerarquía del DOM, permitiendo que el padre escuche los eventos de sus hijos. 3. **toggle()**: Método para agregar o eliminar una clase (ej. 'highlight') de un elemento, permitiendo cambios visuales al hacer clic. 4. **Uso de `closest()`**: Permite encontrar el elemento padre más cercano que coincida con un selector, útil para manejar eventos en una estructura DOM compleja.
    el método closest() no de supone que busca hacia arriba el elemento más cercano???, no termino de entender cómo funciona aquí ?
    El concepto de bubbling se refiere a la propagación de eventos en el DOM. Cuando un elemento recibe un evento, este se "burbujea" hacia arriba en la jerarquía del DOM, permitiendo que los elementos padres, pero no los hermanos, escuchen el mismo evento. Así, solo los padres pueden reaccionar al evento, no los elementos hermanos del elemento que está escuchando. Esto se traduce en que al hacer clic en un elemento hijo, solo los padres pueden manejar ese evento, como se explicó en el patrón de delegación.
    Aca mi codigo, con la version que hice y las otras del video: ```js const allList = document.querySelectorAll('li') const father = document.querySelector('ul') //Mi version: allList.forEach((list) => { list.addEventListener('click', () => { list.classList.toggle('active') }) }); //teacher way allList.forEach((list) => { list.addEventListener('click', (event) => { event.target.classList.toggle('active') }) }) //Version final father.addEventListener('click', (event) => { const mainlist = event.target.closest('li') if (mainlist) { mainlist.classList.toggle('active') } }) ```
    Tal parece que esto es muy util en no solo listas, sino menus , formularios, galerias de imagenes, que necesitan dinamismo, parecese ser , que los elementos añadidos Dinammicamente tendran un manejo del foco mejor si se usa la delegacion de eventos, y sera maas facil mantener la consistencia del contenido, 2 aspectos cruciales en la accesibilidad del contenido (los patrones de interaccion predecibles son fundamentales para una interfaz amigable)