Delegated events en jQuery

  • Por
Los delegated events de jQuery permiten asociar comportamientos a elementos que aún no se encuentran en la página, usando el mismo método on(), aunque definiendo el selector donde quieres estos eventos delegados.

Antes de jQuery 1.7 utilizábamos live() para poder definir eventos con respecto a un selector y que el sistema estuviera pendiente sobre si aparecían nuevos elementos respondiendo a ese selector en la página, a los que le asignaría el evento automáticamente.

Desde hace tiempo se han simplificado las cosas y como sabemos, la librería ha dejado obsoletas varias funciones de eventos y concentrado su uso en el método "on()". En este artículo veremos un uso útil, nada difícil, pero que a veces es desconocido y provoca dudas.

¿Por qué eventos delegados (delegated events)?

Veamos para qué sirven los delegated events de una manera sencilla, por medio de un caso práctico. Surge de una duda que tenía nuestro compañero Chris:

Tengo un problemilla con jQuery: voy agregando elementos a una página, insertando divs. A cada elemento agregado le pongo una etiqueta A por si lo quieren borrar, pero el evento "click" que asigno a los enlaces no se agrega dinámicamente. No reconoce el clic, o sea, no pasa nada.

Dicho de otro modo, Chris inyecta código en la página. Los elementos que inyecta deberían responder a eventos. Pero los eventos definidos no funcionan, porque su creación es anterior a la existencia de esos elementos, es decir, definió el evento "click" previamente a la inyección del elemento sobre el que se tenía que asignar ese evento.

La solución bruta es la siguiente:
Cuando inyectes un código, si hay elementos que quieras que respondan a eventos, genera esos comportamientos de respuesta a los eventos, justo después de haberlos inyectado. O sea, después de hacer el "append()", haces el "on()" sobre los elementos que acabas de insertar que quieras que respondan a eventos.

Esta solución no es del todo bonita, porque tienes que estar siempre pendiente de qué se inserta en la página, para ver si le tienes que asignar eventos. Sobre todo en esquemas de páginas altamente dinámicas, donde se inyecta HTML desde varios lugares de tu Javascript, puede ser un jaleo.

La solución elegante son los eventos delegated:
Lo que querrás hacer es definir tus eventos una vez, quizás en el "document ready", y que esta definición afecte a todos los elementos existentes actualmente y a los que se puedan inyectar en el futuro, ¿no?

Afortunadamente es bien fácil.

Cómo hacer un delegated event

El mecanismo es bien simple, lo verás con un ejemplo. Tienes un código como este:

<div id="productos">
<div class="prod">
Producto predeterminado
<br>
<a href="#" class="cerrarprod">cerrar</a>
</div>
</div>

En la mayoría de los casos, si quieres que el producto se borre al pulsar sobre el enlace "cerrar", quizás defines un evento así:

$(".cerrarprod").on("click", function(e){
e.preventDefault()?
$(this).parent().remove()?
})?

Eso funcionará perfectamente, sobre todos los enlaces de la class="cerrarprod" que actualmente haya en el documento HTML. El problema es que luego inyectes otros productos:

$("#productos").append('<div class="prod">' + nombreProd + '<br><a href="#" class="cerrarprod">cerrar</a></div>')

Nota: Inyectar código de esa manera no es muy "elegante". Si quieres hacerte un favor a ti mismo, trata de usar algún sistema de plantillas Javascript.

Esos nuevos productos tienen el enlace de "cerrar", pero lamentablemente no funcionará tu evento.

En lugar de definir el evento de la anterior manera, echa un vistazo al código siguiente.

$("#productos").on("click", "a.cerrarprod", function(e){
e.preventDefault()?
$(this).parent().remove()?
})?

Este sería el método correcto de usar estos eventos delegados. Lo analizamos:

    1) En lugar de asignar el evento a los enlaces directamente (que realmente no tienen por qué estar todos definidos en el HTML, ya que más adelante podremos inyectar código), los asignamos a un elemento padre superior que sí que exista actualmente. "#productos".

    2) Én el método on() indicamos como parámetro un selector. Ese selector será aquellos elementos donde quieres que se definan los delegated events. Osea, en este caso, los enlaces de la clase "cerrarprod".

Es así de simple. De este modo consigues que cualquier enlace en la división id="productos" que pueda tener la clase "cerrarprod" y que exista en el momento actual o en cualquier momento futuro, tenga asignado ese comportamiento de evento.

Conclusión

Yo lo veo superclaro. En vez de asignar el evento a elementos que realmente no sabes si están todos en la página, porque más adelante puedan ser añadidos con métodos de jQuery como "append()", "appendTo()", "prepend()" o similares, lo asignas a un elemento que sí existe (podría ser incluso el propio "document"). Luego le indicas como parámetro en el método "on()" el selector donde realmente quieres que esos eventos estén asignados ahora y en elementos futuros que pueda haber.

¡Pruébalo! ¿lo has conseguido? publica tu código como comentario y ayuda a los demás.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

gerar_pach

07/2/2014
como hacer delegated cuando usamos un plugin
Hola he tenido algunos detalles que no he investigado bien, cuando utilizo un plugin que tiene que ser cargando junto con el documento, pero los elementos que lo requieran aun no estan presentes, como debo hacer ese delegat por ejmplo un plugin de validacion de campos
alphanumerico

esto lo cargo en el js al cargar el documento, pero el formulario aun no
esta presente porque lo anexo con ajax como puedo hacer el delegat usando el script del plugin
saludos!!

csalazart

12/2/2014
Excelente..
La verdad es que mas claro imposible si esta super fácil y Tiene mucho sentidoo... ya que desde que existe Jquery el Nos Ha dicho que se aplican condiciones luego de un document.ready cuando esta completamente cargado y claro el DOM esta completo... así el puede $ puede interactuar con los elementos que existen.. pero si es lógico escojes los Hijos de un padre no sabes cuantos hijos podrá tener o Hilos entonces abarca a todos los que estén o los que podrían estar..es genial simplemente genial..

GrijalvaRomero

17/2/2014
Perfectamente entendible
aunque estaba mas cómodo con la versión .live(), pero ya con esta explicación me ha quedado super claro :)

Luis Saraza

23/9/2014
Excelente
No hay mejor manera de explicarlo, simplemente excelente! Felicidades

Seak

17/5/2016
Delegar a document
Hola, Miguel Angel!
Ves descabellado delegar directamente a document (como apuntas al final de tu post)?
Para mí es super cómodo, pues como bien explicas, el alto contenido dinámico hace hacer chapuzas de programación que no son nada deseables.
Entiendo lo de delegar contra un contenedos superior, pero me pregunto si seria un LAG demasiado alto delegando a document.
En casos puntuales de optimización de rendimiento, si que lo haría más afinado, pero en páginas habituales, la optimización no es nada necesaria y sí la legibilidad del código.

David

23/7/2017
Explicacion clara
Excelente!! te lo agradezco, y esta mejor explicado que otros posts similares.

Saludos!