2012-01-11 17 views
13

tengo algunas marcas como esto (las clases son sólo para la explicación):delegación de eventos vs unión directa al añadir elementos complejos a una página

<ol id="root" class="sortable"> 
    <li> 
    <header class="show-after-collapse">Top-Line Info</header> 
    <section class="hide-after-collapse"> 
     <ol class="sortable-connected"> 
     <li> 
      <header class="show-after-collapse">Top-Line Info</header> 
      <section class="hide-after-collapse"> 
      <div>Content A</div> 
      </section> 
     </li> 
     </ol> 
    </section> 
    </li> 
    <li> 
    <header/> 
    <section class="hide-after-collapse"> 
     <ol class="sortable-connected"> 
     <li> 
      <header/> 
      <section class="hide-after-collapse"> 
      <div>Content B</div> 
      </section> 
     </li> 
     </ol> 
    </section> 
    </li> 
</ol> 

es decir, anidado listas pueden ordenar. El plugin ordenable es suficiente, sin embargo, ya que cada li (en lo sucesivo, "elemento") mantiene su nivel, aunque las listas internas están conectadas. Los elementos tienen un encabezado siempre visible y una sección visible cuando están en estado expandido, al hacer clic en el encabezado. El usuario puede agregar y eliminar elementos de cualquier nivel a voluntad; agregar un elemento de nivel superior incluirá una lista de nidos vacía dentro de él. Mi pregunta es con respecto a JS inicialización del elemento recién creado: Mientras que compartirán algunas funciones comunes, que puedo cubrir a través de

$("#root").on("click", "li > header", function() { 
    $(this).parent().toggleClass("collapsed"); 
}); 

y

li.collapsed section { 
    display: none; 
} 

(pregunta al margen: sería esto una lugar apropiado para usar los detalles/etiquetas HTML5 de resumen? Parece algo dudoso sobre si esos incluso llegarán a la especificación final, y quiero una transición deslizante, por lo que parece que necesitaría JS para eso de todos modos. lanza la pregunta a las masas. Hola, misas.)

Si la lista raíz es el único elemento (relevante) que se garantiza que existe al cargar la página, para que .on() funcione de manera efectiva, debo vincular todos los eventos a ese elemento y deletrear el selector preciso para cada uno, como Yo lo entiendo. Entonces, por ejemplo, para unir funciones separadas a dos botones uno al lado del otro, tendré que deletrear el selector por completo cada vez, & agrave; la

$("#root").on("change", "li > section button.b1", function() { 
    b1Function(); 
}).on("change", "li > section button.b2", function() { 
    b2Function(); 
}); 

¿Es eso exacto? Siendo ese el caso, ¿tiene más sentido renunciar a .on() y vincular mis eventos en el momento en que se agrega el nuevo elemento a la página? El número total de elementos probablemente se sumará a docenas como máximo, si eso hace una diferencia en la respuesta.

+0

+1, me gustaría ver una respuesta detallada tal vez describiendo las consideraciones de rendimiento. – jondavidjohn

+0

En mi opinión, cualquier enfoque estaría bien si sabes que el límite superior es docenas como máximo. Pero estoy seguro de que sabes que a veces estas cosas tienen una forma de crecer con el tiempo ... – nnnnnn

+0

¿Habrá docenas de elementos en total o puede haber docenas de un solo tipo de elemento?En su ejemplo 'button.b2', ¿habrá uno de estos o potencialmente docenas? –

Respuesta

39

Creará menos sobrecarga de CPU al vincular los eventos utilizando $(<root-element>).on(<event>, <selector>) ya que se vinculará a un único elemento "raíz" en lugar de potencialmente muchos más elementos descendientes individuales (cada vinculación lleva tiempo ...).

Dicho esto, incurrirá en más sobrecarga de CPU cuando ocurran los eventos reales, ya que tienen que subir el DOM al elemento "raíz".

de cuentos: delegado ahorra CPU cuando se une controladores de eventos; el enlace salva la CPU cuando los eventos activan (por ejemplo, un usuario hace clic en algo).

Depende de usted decidir qué punto es más importante para el rendimiento. ¿Tiene CPU disponible cuando agrega los nuevos elementos? Si es así, vincular directamente a los nuevos elementos sería lo mejor para el rendimiento general; sin embargo, si agregar elementos es una operación intensiva de CPU, probablemente desee delegar el enlace de evento y permitir que el evento desencadene una sobrecarga de CPU adicional de todo el burbujeo.

Tenga en cuenta que:

$(<root-element>).on(<event>, <selector>, <event-handler>) 

es lo mismo que:

$(<root-element>).delegate(<selector>, <event>, <event-handler>) 

y que:

$(<selector>).on(<event>, <event-handler>) 

es lo mismo que:

$(<selector>).bind(<event>, <event-handler>) 

.on() es nuevo en jQuery 1.7 y si está utilizando 1.7+, entonces .delegate(<selector>, <event>, <event-handler>) es solo un atajo para .on(<event>, <selector>, <event-handler>).

ACTUALIZACIÓN

Aquí es una prueba de rendimiento que muestra que es más rápido para delegar la unión que se una a cada elemento individual evento: http://jsperf.com/bind-vs-click/29. Lamentablemente, esta prueba de rendimiento ha sido eliminada.

ACTUALIZACIÓN

Aquí es una prueba de rendimiento que muestran que evento de activación es más rápido cuando se enlaza directamente a los elementos en lugar de delegar la unión: http://jsperf.com/jquery-delegate-vs-bind-triggering (Tenga en cuenta que esto no es una prueba de rendimiento perfecta ya que los métodos de encuadernación están incluidos en la prueba, pero desde delegate corre más rápido sobre la unión sólo significa que bind es aún más rápido relativamente cuando se habla de activación)

+0

Gracias, esto es genial. Creo que la vinculación directa tiene más sentido en mi caso, ya que los eventos activados en un elemento existente ocurrirán con más frecuencia que la adición de un nuevo elemento. Además, esto me permitirá ser más inteligente con los selectores. – slk

+0

Echa un vistazo a las pruebas de rendimiento. Hacer clic en cada elemento encuadernado es engañoso ya que es probable que los usuarios no lo hagan. El delegado es, con mucho, el mejor actor desde una perspectiva de carga inicial. –

+0

@JECarterII No creo muchas pruebas de rendimiento, pero pensé que la idea era mostrar el% de diferencia en el rendimiento al hacer clic en enlaces. Al hacer clic en más enlaces se obtiene un conjunto de datos mayor que debería proporcionar un promedio estadístico más preciso, ¿correcto? ¿O me estoy perdiendo algo? También pensé que usar '.find()' en ambas pruebas era bueno porque no sesgaba el rendimiento solo usando '.find()' en una prueba y no en la otra, razón por la cual tu prueba tiene una brecha de rendimiento tan grande Creo. – Jasper

6

Dado que la respuesta aceptada tiene pruebas inexactas (por cierto: prueba de su código, medir el rendimiento, don Solo a ciegas bajo algunas "reglas" - esto no es cómo se realiza la optimización) y es simplemente un error que publico pruebas fijas:! https://jsperf.com/jquery-delegate-vs-bind-triggering/49

que demuestra en tal ejemplo sencillo que no hay diferencia entre la delegación o unión directa

Los únicos casos en que la delegación siempre es mala son eventos como el movimiento del mouse y desplazamiento, que se desencadenan x veces por segundo. ESTO es donde notará cualquier diferencia de rendimiento.

Si tiene incluso 1ms de diferencia (no sucederá, pero esto es solo un ejemplo) en un solo evento, como hacer clic, no lo notará. Si tiene una diferencia de 1 ms en un evento que ocurre 100 veces en un segundo, notará el consumo de CPU.

El simple hecho de tener miles de elementos no afectará negativamente a su rendimiento con la delegación, en realidad, este es el caso cuando deberían utilizarse, para evitar acaparar la CPU cuando se conectan miles de controladores de eventos.

Así que si realmente necesita una regla a seguir (no haga eso): use la delegación en todo excepto en el movimiento del mouse, desplazamiento y otros eventos que puede esperar disparar continuamente.

Cuestiones relacionadas