miércoles, 30 de mayo de 2012

Diferencias entre bind, live, delegate y on

Está claro que una de las partes fundamentales de jQuery es el manejo de eventos, pero cuando tenemos que ponernos manos a la obra se nos presentan distintas opciones y no siempre sabemos cual es la idónea.

A nuestra disposición tenemos diversas funciones que aparentemente hacen lo mismo:

·         bind

·         live

·         delegate

·         on

El porqué de esta diversidad es fruto de querer hacer una mejor librería jQuery a la vez que se mantiene retrocompatibilidad con versiones anteriores del producto. Aquí está claro que no todos los proyectos pueden cambiar de jQuery 1.0 a jQuery 1.7.2 sin que ello conlleve una refactorización de código de magno esfuerzo.

Cronológicamente hablando (y espero no equivocarme), la primera función en aparecer en escena fue bind. Después apareció live para cubrir un hueco que no llenaba bind (la delegación de eventos), pero enseguida vieron que live no funcionaba como se esperaba de ella, así que incorporaron delegate en detrimento de live. Por último y para evitar que la gente se volviera loca teniendo que decidir si utilizar bind, live o delegate, surgió on, que en un solo método reúne la funcionalidad de todos los métodos anteriores.

Una vez que ya tenemos claro el orden cronológico de aparición de todos estos métodos, la siguiente pregunta a contestar sería ¿Por qué apareció live? ¿Qué es eso de la delegación que bind no podía hacer?

Con bind podemos adjuntar manejadores de eventos (o callbacks en terminología javascript) a elementos existentes en el DOM. Lo cierto es que bind siempre ha funcionado maravillosamente bien pero tiene un grave problema cuando nos encontramos en escenarios donde el DOM cambia dinámicamente (por ejemplo aplicaciones con AJAX). El problema está en que bind adjunta manejadores sólo a elementos existentes, es decir, futuros elementos que se incorporen al DOM no sabrán nada de los eventos manejados por bind (aunque coincidan con el selector suministrado a bind).

Por ejemplo pensemos que tenemos un ul con un solo li hijo. Adjuntamos un manejador al evento click en los li hijos del elemento ul (no sólo para el único hijo existente, sino en general para todos los hijos li de ul). Cada vez que hagamos click en un li agregaremos un nuevo li al ul padre. ¿Si hacemos click en los nuevos elementos li creados dinámicamente se disparará nuestro manejador? Pues no, porque bind adjuntó el evento sólo a elementos existentes en el momento de su declaración, así que nuevos elementos que coinciden con nuestro selector de bind no se darán por aludidos.

    <ul id="lista">

        <li>1</li>

    </ul>


$(document).ready(function () {

    $("#lista li").bind("click", function () {

        var ul = $(this).parent();

        var length = ul.children().length;

        // aunque el nuevo li coincide con el selector $("#lista li"), bind no funcionará con el nuevo elemento recién agregado

        ul.append("<li>" + (length + 1) + "</li>");

    });

});


Lógicamente tenemos un problema con este código ¿Cómo adjuntar o re-adjuntar nuestro manejador a los nuevos elementos li? Pues para eso apareció live.

Live funciona con la delegación de eventos, es decir, es vez de adjuntar el manejador click al único elemento li presente que cumplía el selector $("#lista li") en el momento de la declaración, lo que hace es declarar un manejador global a elementos que coincidan con el selector, esto es tanto a elementos que existen ahora como elementos que vayan a existir en el futuro.

Veamos ahora que nuestro código sólo requiere un simple cambio para trabajar con live en vez de con bind.

$("#lista li").live("click",

 

A partir de este momento, cualquier elemento li que sea hijo de #lista tendrá adjunto el manejador de evento especificado (danto igual si el hijo existía en el momento de la declaración del manejador o fue creado después de forma dinámica).

El principal problema del que adolece live es que para hacer funcionar la delegación de eventos, adjunta el manejador al elemento document. De este forma, cada vez que se haga click en el documento (en cualquier sitio del documento) se chequeará si el elemento clicado coincide con el selector $("#lista li"), y si coincide se lanzará el evento.

Para solucionar el cómo funciona live, rápidamente se incorporó delegate.

Delegate respeta el mismo principio de delegación de eventos que live, pero a diferencia de éste, delegate adjunta el manejador a un padre específico. De esta forma ya no podemos hablar de un manejador global, sino de un manejador local con un ámbito concreto.

Para adaptar nuestro código a delegate, tenemos ahora que especificar un padre y un selector de hijos que se validarán ahora y en el futuro:

$("#lista").delegate("li", "click", function () { …

 

Si nos fijamos, con delegate hemos especificado que queremos adjuntar el manejador click a todos los hijos li (ahora y el futuro) para el padre #lista. Y todo esto confinado a un padre, es decir, sólo se chequeará el selector si el click sucede dentro de #lista, no a nivel de documento como sucedía con live.

Llegados a este punto está claro que hemos descartado live y sólo nos quedaría elegir entre bind y delegate según el escenario, pero lo cierto es que utilizando la nueva función on podemos olvidarnos de todo lo anterior y tener en un solo método toda la funcionalidad de bind y delegate.

Por ejemplo nuestro código anterior con bind pasaría a ser:

$("#lista li").on("click", function () { …


Y nuestro anterior delegate sería ahora:

$("#lista").on("click", "li", function () { …

 

Como vemos, con on podemos tanto gestionar eventos al estilo de bind (sólo para elementos existentes) como al estilo de delegate (con delegación de eventos para elementos futuros).

Ya no tienes excusa… utiliza on ... que no lo digo yo, lo dice jQuery!

Un saludo!

20 comentarios:

  1. Buenísima explicación, tío.

    ResponderEliminar
  2. Sin duda muy buen artículo.

    ResponderEliminar
  3. Eres el mejor!! te quiero un monton que lo sepas!! att:Santiago Blanco!

    ResponderEliminar
  4. Gracias me sirvio mucho saludos desde bogota.

    ResponderEliminar
  5. Muchas gracias por la explicación. Clara y sin vueltas. Saludos.

    ResponderEliminar
  6. La mejor explicación que he leído sobre este jaleo. Gracias.

    ResponderEliminar
  7. Muy Buen aporte Muchas Gracias me funciono de Mucho

    !!!!Gracias¡¡¡

    ResponderEliminar
  8. Esta claro que muchas veces es tan importante una cierta tecnología como alguien tan capaz como tu para saber explicarla. Excelente aclaración. Tu aportacion nos hace mas capaces. Gracias.

    ResponderEliminar
  9. Hola. Tengo un problema: yo con un evento click transformo un texto dentro de un td en un "input" y luego a través de otra función jquery quiero actuar sobre ese "input" creado dinámicamente, pero aunque le aplico "on", no funciona, ¿qué se puede hacer?

    Saludos.

    ResponderEliminar
  10. Muy muy bien explicado. Gracias.

    ResponderEliminar
  11. Muchas gracias por quitarme esa duda Saludos...

    ResponderEliminar
  12. Fenomenal la explicación, muchas gracias por el tiempo que has dedicado a ayudar al que quiere aprender, saludos!!!

    ResponderEliminar
  13. Muy bueno, buena explicación, sencillo y me ha servido perfecto. Hacia tiempo que no me encontraba artículos tan geniales.
    Gracias!

    ResponderEliminar
  14. Que buena explicación, gracias por el aporte!!

    ResponderEliminar
  15. Me salvaste!!!! Gracias!!

    ResponderEliminar