Directo vs. Delegado - jQuery .on ()

159

Estoy tratando de comprender esta diferencia específica entre las directas y delegadas controladores de eventos utilizando el jQuery .on () método . Específicamente, la última oración en este párrafo:

Cuando selectorse proporciona a, el controlador de eventos se denomina delegado . No se llama al controlador cuando el evento ocurre directamente en el elemento enlazado, sino solo para los descendientes (elementos internos) que coinciden con el selector. jQuery burbujea el evento desde el destino del evento hasta el elemento donde está conectado el controlador (es decir, el elemento más interno al más externo) y ejecuta el controlador para cualquier elemento a lo largo de esa ruta que coincida con el selector.

¿Qué quiere decir con "ejecuta el controlador para cualquier elemento"? Hice una página de prueba para experimentar con el concepto. Pero las dos construcciones siguientes conducen al mismo comportamiento:

$("div#target span.green").on("click", function() {
   alert($(this).attr("class") + " is clicked");
});

o,

$("div#target").on("click", "span.green", function() {
   alert($(this).attr("class") + " is clicked");
});

¿Quizás alguien podría referirse a un ejemplo diferente para aclarar este punto? Gracias.

moey
fuente
77
Para todos los interesados: jsperf.com/jquery-fn-on-delegate-vs-direct
dgo
1
@KevinWheeler Comenté tu violín a continuación, pero aquí , esencialmente, no está configurado correctamente (estás vinculando al elemento padre, y la delegación está destinada a los hijos). Para responder a su pregunta, significa que el controlador delegado coincidirá con los elementos recién agregados, donde el que no tiene delegación no lo hará. La delegación tiene la ventaja de que hay menos eventos conectados al navegador que causan un menor consumo de memoria para la aplicación, sin embargo, la desventaja es que aumenta el tiempo para procesar un clic (como mínimo). Si estás haciendo un juego, no delegues.
vipero07
1
La "página de prueba" a la que hace referencia no funciona.
Jaime Montoya

Respuestas:

373

Caso 1 (directo):

$("div#target span.green").on("click", function() {...});

== ¡Hola! Quiero que cada span.green dentro del div # target escuche: cuando te hagan clic, haz X.

Caso 2 (delegado):

$("div#target").on("click", "span.green", function() {...});

== ¡Hola, div # target! Cuando haga clic en cualquiera de los elementos secundarios que son "span.green", haga una X con ellos.

En otras palabras...

En el caso 1, cada uno de esos tramos ha recibido instrucciones individuales. Si se crean nuevos tramos, no habrán escuchado las instrucciones y no responderán a los clics. Cada lapso es directamente responsable de sus propios eventos.

En el caso 2, solo el contenedor ha recibido las instrucciones; es responsable de notar los clics en nombre de sus elementos secundarios. El trabajo de captura de eventos ha sido delegado . Esto también significa que la instrucción se llevará a cabo para los elementos secundarios que se creen en el futuro.

N3dst4
fuente
45
Esa es una gran explicación, y ha traído claridad a un problema que siempre me he negado a entender. ¡Gracias!
dgo
3
Entonces, ¿por qué on()permite dos argumentos cuando eso sería lo mismo que usar click()?
nipponese
55
.on () es una API de propósito general que puede manejar cualquier tipo de evento, incluidos múltiples eventos diferentes (puede poner varios nombres de eventos en esa primera cadena). .click () es solo una abreviatura para esa primera forma.
N3dst4
1
@newbie, @ N3dst4: e.targetserá el objetivo inicial del evento de clic (puede ser un nodo hijo si span.greentiene hijos). Desde el interior del controlador, debe usar la thisreferencia. Ver este violín .
LeGEC
1
Otra nota para agregar al tema de la delegación: es muy útil y muy eficiente. Utilizará menos recursos del navegador con delegación frente a adjuntar un evento a cada elemento coincidente. Pensé en mencionarlo, por si la gente necesitaba más razones para usar la delegación.
Phatskat
6

La primera forma, $("div#target span.green").on()vincula un controlador de clics directamente a los intervalos que coinciden con el selector en el momento en que se ejecuta el código. Esto significa que si se agregan otros tramos más tarde (o si se cambia su clase para que coincida), se han perdido y no tendrán un controlador de clics. También significa que si luego elimina la clase "verde" de uno de los tramos, su controlador de clics continuará ejecutándose: jQuery no realiza un seguimiento de cómo se asignó el controlador y verifica si el selector aún coincide.

La segunda forma, $("div#target").on()vincula un controlador de clic a los div (s) que coinciden (de nuevo, esto es contra los que coinciden en ese momento), pero cuando se produce un clic en algún lugar del div, la función del controlador solo se ejecutará si el clic ocurrió no solo en el div sino en un elemento hijo que coincide con el selector en el segundo parámetro a .on()"span.green". Hecho de esta manera, no importa cuándo se crearon esos tramos secundarios, al hacer clic en ellos se ejecutará el controlador.

Entonces, para una página que no agrega o cambia dinámicamente su contenido, no notará una diferencia entre los dos métodos. Si agrega dinámicamente elementos secundarios adicionales, la segunda sintaxis significa que no tiene que preocuparse por asignarles controladores de clics porque ya lo hizo una vez en el elemento primario.

nnnnnn
fuente
5

La explicación de N3dst4 es perfecta. En base a esto, podemos suponer que todos los elementos secundarios están dentro del cuerpo, por lo tanto, solo necesitamos usar esto:

$('body').on('click', '.element', function(){
    alert('It works!')
});

Funciona con evento directo o delegado.

Brynner Ferreira
fuente
2
jquery desaconseja el uso del cuerpo, ya que es más lento, porque el script debería buscar todos los niños dentro del cuerpo, lo que debería ser mucho en la mayoría de los casos. Es mejor (más rápido) usar el contenedor primario inmediato del elemento.
mikesoft
1
Jquery eliminó el método en vivo que estaba haciendo lo mismo que mostraste. Este es un rendimiento débil y no debe usarse.
Maciej Sikora
En los navegadores modernos, la $('body').on()delegación de .elementdebería comportarse exactamente igual que la nativa document.body.addEventHandler()con un if (Event.target.className.matches(/\belement\b/))en la devolución de llamada. Puede ser un poco más lento en jquery debido a la $.proxysobrecarga, pero no me cite al respecto.
cowbert
2

Tangencial al OP, pero el concepto que me ayudó a desentrañar la confusión con esta característica es que los elementos vinculados deben ser padres de los elementos seleccionados .

  • Atado se refiere a lo que queda de la .on.
  • Seleccionado se refiere al segundo argumento de .on().

La delegación no funciona como .find (), seleccionando un subconjunto de los elementos enlazados. El selector solo se aplica a elementos secundarios estrictos.

$("span.green").on("click", ...

es muy diferente de

$("span").on("click", ".green", ...

En particular, para obtener las ventajas @ N3dst4 sugiere "elementos que se crearán en el futuro", el elemento enlazado debe ser un padre permanente . Entonces los niños seleccionados pueden ir y venir.

EDITAR

Lista de verificación de por qué delegado .onno funciona

Razones difíciles por las que $('.bound').on('event', '.selected', some_function)puede no funcionar:

  1. El elemento unido no es permanente . Fue creado después de llamar.on()
  2. El elemento seleccionado no es un hijo propio de un elemento enlazado. Es el mismo elemento.
  3. El elemento seleccionado evitó la propagación de un evento al elemento vinculado mediante una llamada .stopPropagation().

(Omitiendo razones menos difíciles, como un selector mal escrito).

Bob Stein
fuente
1

Escribí una publicación con una comparación de eventos directos y delegados. Comparo js puro pero tiene el mismo significado para jquery que solo lo encapsula.

La conclusión es que el manejo de eventos delegados es para una estructura DOM dinámica donde se pueden crear elementos enlazados mientras el usuario interactúa con la página (sin necesidad de enlaces nuevamente), y el manejo directo de eventos es para elementos DOM estáticos, cuando sabemos que la estructura no cambiará.

Para obtener más información y una comparación completa: http://maciejsikora.com/standard-events-vs-event-delegation/

El uso de controladores siempre delegados, que veo que está muy de moda actualmente, no es la forma correcta, muchos programadores lo usan porque "debería usarse", pero la verdad es que los controladores de eventos directos son mejores para alguna situación y la elección de qué método usar debe ser compatible por conocimiento de las diferencias.

Maciej Sikora
fuente
si se adjuntan muchos controladores de eventos (por ejemplo, cada fila de una tabla), a menudo es mejor usar un controlador delegado único en el contenedor en lugar de adjuntar muchos controladores directos. Puede ver esto desde el hecho básico de que el recuento de controladores de eventos es en sí mismo una métrica de creación de perfiles.
cowbert