¿Todos los eventos de jquery deberían estar vinculados a $(documento)?
De donde viene esto
Cuando aprendí jQuery por primera vez, normalmente adjuntaba eventos como este:
$('.my-widget a').click(function() {
$(this).toggleClass('active');
});
Después de aprender más sobre la velocidad del selector y la delegación de eventos, leí en varios lugares que "la delegación de eventos de jQuery hará que su código sea más rápido". Entonces comencé a escribir código como este:
$('.my-widget').on('click','a',function() {
$(this).toggleClass('active');
});
Esta también fue la forma recomendada de replicar el comportamiento del evento .live() obsoleto. Lo cual es importante para mí, ya que muchos de mis sitios agregan/eliminan widgets dinámicamente todo el tiempo. Sin embargo, lo anterior no se comporta exactamente como .live(), ya que solo los elementos agregados al contenedor ya existente '.my-widget' obtendrán el comportamiento. Si agrego dinámicamente otro bloque de html después de que se haya ejecutado ese código, esos elementos no tendrán los eventos vinculados a ellos. Como esto:
setTimeout(function() {
$('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);
Lo que quiero lograr:
- el antiguo comportamiento de .live() // lo que significa adjuntar eventos a elementos que aún no existen
- los beneficios de .on()
- rendimiento más rápido para vincular eventos
- Una forma sencilla de gestionar eventos
Ahora adjunto todos los eventos como este:
$(document).on('click.my-widget-namespace', '.my-widget a', function() {
$(this).toggleClass('active');
});
Lo cual parece cumplir todos mis objetivos. (Sí, es más lento en IE por alguna razón, ¿no tienes idea de por qué?) Es rápido porque solo un evento está vinculado a un elemento singular y el selector secundario solo se evalúa cuando ocurre el evento (corrígeme si esto está mal aquí). El espacio de nombres es fantástico ya que facilita alternar el detector de eventos.
Mi solución/pregunta
Entonces estoy empezando a pensar que los eventos jQuery siempre deberían estar vinculados a $(documento).
¿Hay alguna razón por la que no querrías hacer esto?
¿Podría considerarse esto una mejor práctica? Si no, ¿por qué?
Si has leído todo esto, gracias. Agradezco cualquier/todos los comentarios/ideas.
Supuestos:
- Usando jQuery que admita
.on()
// al menos la versión 1.7 - Quiere que el evento se agregue al contenido agregado dinámicamente
Lecturas/Ejemplos:
- http://24ways.org/2011/your-jquery-now-with-less-suck
- http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
- http://www.jasonbuckboyer.com/playground/speed/speed.html
- http://api.jquery.com/on/
No, NO debe vincular todos los controladores de eventos delegados al document
objeto. Ese es probablemente el escenario de peor rendimiento que podría crear.
En primer lugar, la delegación de eventos no siempre hace que su código sea más rápido. En algunos casos, es ventajoso y en otros no. Debe utilizar la delegación de eventos cuando realmente la necesite y cuando se beneficie de ella. De lo contrario, debes vincular los controladores de eventos directamente a los objetos donde ocurre el evento, ya que esto generalmente será más eficiente.
En segundo lugar, NO debe vincular todos los eventos delegados a nivel de documento. Esta es exactamente la razón por la que .live()
quedó obsoleto porque es muy ineficiente cuando tienes muchos eventos vinculados de esta manera. Para el manejo de eventos delegados, es MUCHO más eficiente vincularlos al padre más cercano que no sea dinámico.
En tercer lugar, no todos los eventos funcionan ni todos los problemas se pueden resolver con la delegación. Por ejemplo, si desea interceptar eventos clave en un control de entrada y bloquear la entrada de claves no válidas en el control de entrada, no puede hacerlo con el manejo de eventos delegado porque cuando el evento llega al controlador delegado, ya ha ha sido procesado por el control de entrada y es demasiado tarde para influir en ese comportamiento.
Estos son los momentos en los que la delegación de eventos es necesaria o ventajosa:
- Cuando los objetos en los que está capturando eventos se crean/eliminan dinámicamente y aún desea capturar eventos en ellos sin tener que volver a vincular explícitamente los controladores de eventos cada vez que crea uno nuevo.
- Cuando tienes muchos objetos y todos quieren exactamente el mismo controlador de eventos (donde muchos son al menos cientos). En este caso, puede ser más eficaz en el momento de la configuración vincular un controlador de eventos delegado en lugar de cientos o más controladores de eventos directos. Tenga en cuenta que el manejo de eventos delegados siempre es menos eficiente en tiempo de ejecución que los controladores de eventos directos.
- Cuando intenta capturar (en un nivel superior en su documento) eventos que ocurren en cualquier elemento del documento.
- Cuando su diseño utiliza explícitamente la propagación de eventos y stopPropagation() para resolver algún problema o característica en su página.
Para comprender esto un poco más, es necesario comprender cómo funcionan los controladores de eventos delegados de jQuery. Cuando llamas a algo como esto:
$("#myParent").on('click', 'button.actionButton', myFn);
Instala un controlador de eventos jQuery genérico en el #myParent
objeto. Cuando un evento de clic aparece en este controlador de eventos delegado, jQuery tiene que revisar la lista de controladores de eventos delegados adjuntos a este objeto y ver si el elemento de origen del evento coincide con alguno de los selectores en los controladores de eventos delegados.
Debido a que los selectores pueden ser bastante complicados, esto significa que jQuery tiene que analizar cada selector y luego compararlo con las características del objetivo del evento original para ver si coincide con cada selector. Esta no es una operación barata. No es gran cosa si solo hay uno de ellos, pero si coloca todos sus selectores en el objeto del documento y hay cientos de selectores para comparar con cada evento burbujeado, esto puede comenzar a afectar seriamente el rendimiento del manejo de eventos.
Por este motivo, desea configurar sus controladores de eventos delegados de modo que un controlador de eventos delegado esté lo más cerca posible del objeto de destino. Esto significa que pasarán menos eventos por cada controlador de eventos delegado, lo que mejorará el rendimiento. Poner todos los eventos delegados en el objeto del documento es el peor rendimiento posible porque todos los eventos burbujeados tienen que pasar por todos los controladores de eventos delegados y ser evaluados con todos los posibles selectores de eventos delegados. Esta es exactamente la razón por la que .live()
está en desuso porque esto es lo que .live()
sucedió y resultó ser muy ineficiente.
Entonces, para lograr un rendimiento optimizado:
- Utilice el manejo de eventos delegado solo cuando realmente proporcione una característica que necesita o aumente el rendimiento. No lo uses siempre porque es fácil, porque cuando en realidad no lo necesitas. En realidad, funciona peor en el momento del envío de eventos que el enlace directo de eventos.
- Adjunte controladores de eventos delegados al padre más cercano al origen del evento tanto como sea posible. Si está utilizando el manejo de eventos delegado porque tiene elementos dinámicos para los que desea capturar eventos, seleccione el padre más cercano que no sea dinámico en sí mismo.
- Utilice selectores fáciles de evaluar para controladores de eventos delegados. Si siguió cómo funciona el manejo de eventos delegados, comprenderá que un controlador de eventos delegado debe compararse con muchos objetos muchas veces, por lo que elegir un selector lo más eficiente posible o agregar clases simples a sus objetos para que se puedan usar selectores más simples. aumentar el rendimiento del manejo de eventos delegados.
La delegación de eventos es una técnica para escribir controladores antes de que el elemento exista realmente en DOM. Este método tiene sus propias desventajas y debe usarse sólo si tiene tales requisitos.
¿Cuándo debería utilizar la delegación de eventos?
- Cuando vincula un controlador común para más elementos que necesitan la misma funcionalidad. (Ejemplo: pasar el cursor por la fila de la tabla)
- En el ejemplo, si tuviera que vincular todas las filas mediante enlace directo, terminaría creando n controladores para n filas en esa tabla. Al utilizar el método de delegación, podría terminar manejando todos esos en 1 controlador simple.
- Cuando agrega contenidos dinámicos con más frecuencia en DOM (Ej: agregar/eliminar filas de una tabla)
¿Por qué no debería utilizar la delegación de eventos?
- La delegación de eventos es más lenta en comparación con vincular el evento directamente al elemento.
- Compara el selector de objetivos en cada burbuja que golpea, la comparación será tan costosa como complicada.
- No hay control sobre el evento que se propaga hasta que llega al elemento al que está vinculado.
PD: Incluso para contenidos dinámicos, no es necesario utilizar el método de delegación de eventos si vincula el controlador después de que el contenido se inserta en DOM. (Si se agrega contenido dinámico, no se elimina/vuelve a agregar con frecuencia)
Me gustaría agregar algunos comentarios y contraargumentos a la respuesta de jfriend00. (principalmente solo mis opiniones basadas en mi instinto)
No, NO debe vincular todos los controladores de eventos delegados al objeto del documento. Ese es probablemente el escenario de peor rendimiento que podría crear.
En primer lugar, la delegación de eventos no siempre hace que su código sea más rápido. En algunos casos, es ventajoso y en otros no. Debe utilizar la delegación de eventos cuando realmente la necesite y cuando se beneficie de ella. De lo contrario, debes vincular los controladores de eventos directamente a los objetos donde ocurre el evento, ya que esto generalmente será más eficiente.
Si bien es cierto que el rendimiento podría ser ligeramente mejor si solo vas a registrar un evento para un único elemento, creo que no supera los beneficios de escalabilidad que aporta la delegación. También creo que los navegadores manejarán esto cada vez de manera más eficiente, aunque no tengo pruebas de ello. En mi opinión, ¡la delegación de eventos es el camino a seguir!
En segundo lugar, NO debe vincular todos los eventos delegados a nivel de documento. Esta es exactamente la razón por la que .live() quedó obsoleto porque es muy ineficiente cuando tienes muchos eventos vinculados de esta manera. Para el manejo de eventos delegados, es MUCHO más eficiente vincularlos al padre más cercano que no sea dinámico.
Estoy de acuerdo en esto. Si está 100% seguro de que un evento solo ocurrirá dentro de un contenedor, tiene sentido vincular el evento a este contenedor, pero aún así estaría en contra de vincular eventos directamente al elemento desencadenante.
En tercer lugar, no todos los eventos funcionan ni todos los problemas se pueden resolver con la delegación. Por ejemplo, si desea interceptar eventos clave en un control de entrada y bloquear la entrada de claves no válidas en el control de entrada, no puede hacerlo con el manejo de eventos delegado porque cuando el evento llega al controlador delegado, ya ha ha sido procesado por el control de entrada y es demasiado tarde para influir en ese comportamiento.
Esto simplemente no es cierto. Consulte este códigoPen: https://codepen.io/pwkip/pen/jObGmjq
document.addEventListener('keypress', (e) => {
e.preventDefault();
});
Ilustra cómo se puede evitar que un usuario escriba registrando el evento de pulsación de tecla en el documento.
Estos son los momentos en los que la delegación de eventos es necesaria o ventajosa:
Cuando los objetos en los que está capturando eventos se crean/eliminan dinámicamente y aún desea capturar eventos en ellos sin tener que volver a vincular explícitamente los controladores de eventos cada vez que crea uno nuevo. Cuando tienes muchos objetos y todos quieren exactamente el mismo controlador de eventos (donde muchos son al menos cientos). En este caso, puede ser más eficaz en el momento de la configuración vincular un controlador de eventos delegado en lugar de cientos o más controladores de eventos directos. Tenga en cuenta que el manejo de eventos delegados siempre es menos eficiente en tiempo de ejecución que los controladores de eventos directos.
Me gustaría responder con esta cita de https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c
Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.
Además, buscar en Google performance cost of event delegation google
arroja más resultados a favor de la delegación de eventos.
Cuando intenta capturar (en un nivel superior en su documento) eventos que ocurren en cualquier elemento del documento. Cuando su diseño utiliza explícitamente la propagación de eventos y stopPropagation() para resolver algún problema o característica en su página. Para comprender esto un poco más, es necesario comprender cómo funcionan los controladores de eventos delegados de jQuery. Cuando llamas a algo como esto:
$("#myParent").on('click', 'button.actionButton', myFn); Instala un controlador de eventos jQuery genérico en el objeto #myParent. Cuando un evento de clic aparece en este controlador de eventos delegado, jQuery tiene que revisar la lista de controladores de eventos delegados adjuntos a este objeto y ver si el elemento de origen del evento coincide con alguno de los selectores en los controladores de eventos delegados.
Debido a que los selectores pueden ser bastante complicados, esto significa que jQuery tiene que analizar cada selector y luego compararlo con las características del objetivo del evento original para ver si coincide con cada selector. Esta no es una operación barata. No es gran cosa si solo hay uno de ellos, pero si coloca todos sus selectores en el objeto del documento y hay cientos de selectores para comparar con cada evento burbujeado, esto puede comenzar a afectar seriamente el rendimiento del manejo de eventos.
Por este motivo, desea configurar sus controladores de eventos delegados de modo que un controlador de eventos delegado esté lo más cerca posible del objeto de destino. Esto significa que pasarán menos eventos por cada controlador de eventos delegado, lo que mejorará el rendimiento. Poner todos los eventos delegados en el objeto del documento es el peor rendimiento posible porque todos los eventos burbujeados tienen que pasar por todos los controladores de eventos delegados y ser evaluados con todos los posibles selectores de eventos delegados. Esta es exactamente la razón por la que .live() está en desuso porque esto es lo que hizo .live() y resultó ser muy ineficiente.
¿Dónde está esto documentado? Si eso es cierto, entonces jQuery parece estar manejando la delegación de una manera muy ineficiente, y entonces mis contraargumentos solo deberían aplicarse a Vanilla JS.
Aún así: me gustaría encontrar una fuente oficial que respalde esta afirmación.
:: EDITAR ::
Parece que jQuery está haciendo burbujeo de eventos de una manera muy ineficiente (porque es compatible con IE8) https://api.jquery.com/on/#event-performance
Entonces, la mayoría de mis argumentos aquí solo son válidos para Vanilla JS y los navegadores modernos.
Aparentemente, ahora se recomienda la delegación de eventos. al menos para vanilla js.
https://gomakethings.com/why-event-delegation-is-a-better-way-to-listen-for-events-in-vanilla-js/
"Rendimiento web # Parece que escuchar cada clic en el documento sería malo para el rendimiento, pero en realidad es más eficaz que tener un grupo de detectores de eventos en elementos individuales".