2012-07-11 26 views
8

El siguiente código utiliza el evento de mutación DOM DOMNodeInserted para detectar la existencia del elemento body y ajustar su innerHTML en un contenedor.¿Los observadores de mutación DOM son más lentos que los eventos de mutación DOM?

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
    <script> 
     function DOMmanipulation() { 
      if (document.body) { 
       document.removeEventListener('DOMNodeInserted', DOMmanipulation); 
       // DOM manipulation start 
       document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>'; 
       // DOM manipulation end 
      } 
     } 
     document.addEventListener('DOMNodeInserted', DOMmanipulation); 
    </script> 
</head> 
<body> 
    <p>Lorem ipsum dolor sit amet.</p> 
</body> 
</html> 

Y a pesar del éxito de la envoltura, hay un error que muestra que no se encontró un nodo. This answer de una pregunta explica que es porque cuando se cargó jQuery, se agregó un elemento div en el cuerpo para realizar algunas pruebas, pero no se pudo eliminar ese elemento div porque ese elemento se ha incluido en el contenedor para que no sea un elemento infantil del cuerpo más.

El experimento anterior nos dice que el evento DOMNodeInserted es más rápido que las pruebas de jQuery porque el elemento de prueba de jQuery (div) se completó antes de que jQuery lo elimine.




Ahora el siguiente código puede lograr la misma manipulación, y está utilizando los observadores DOM Mutación de reciente introducción. A partir de este momento (2012-07-11), solo funciona en Chrome 18 y versiones posteriores.

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
    <script> 
     var observer = new WebKitMutationObserver(function() { 
      if (document.body) { 
       observer.disconnect(); 
       // DOM manipulation start 
       document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>'; 
       // DOM manipulation end 
      } 
     }); 
     observer.observe(document, { subtree: true, childList: true }); 
    </script> 
</head> 
<body> 
    <p>Lorem ipsum dolor sit amet.</p> 
</body> 
</html> 

Estos códigos no producen ningún error. Eso significa que jQuery es más rápido que DOM Mutation Observers, por lo que fue capaz de eliminar su elemento de prueba (div) antes de que ese elemento pueda ser envuelto en el contenedor.




A partir de los dos experimentos anteriores, encontramos que cuando se trata de la velocidad de ejecución:

  • DOM Mutación Eventos > pruebas de jQuery
  • pruebas de jQuery > Observadores de mutación DOM

¿Puede este resultado probar que DOM Mutation Observers es más lento que DOM Mutation Events?

Respuesta

24

Los Observadores de Mutación DOM, no están destinados a ser más rápidos que los Eventos de Mutación DOM. Más bien están destinados a ser más eficientes y seguros.

La esencia básica de la diferencia es que los eventos de mutación DOM se disparan cada vez que hay un cambio. Por lo tanto, este código, por ejemplo, crearía un bucle de devolución de llamada, que finalmente bloqueará el navegador.

document.addEventListener('DOMNodeInserted', function() { 
    var newEl = document.createElement('div'); 
    document.body.appendChild(newEl); 
}); 

El hecho de que se llama de esta manera y tan a menudo también tiene un efecto significativo en el navegador, ya que obliga a una interrupción entre los navegadores recalcular estilo, reflujo y volver a pintar ciclo o peores fuerzas del navegador para volver a calcular estilos, reflujo y repintado en cada devolución de llamada. El problema se ve aún más exasperado por el hecho de que otro código puede estar ejecutando cambios adicionales en el DOM, que continuarán interrumpidos por la devolución de llamada.

Además, debido a que los eventos se propagan de la misma forma que los Eventos DOM normales, comenzará a escuchar cambios en elementos que podrían no interesarle o que no explican en su código. Por lo tanto, todo el mecanismo de los eventos de mutación DOM puede ser problemático para administrar con bastante rapidez.

Los observadores de mutación DOM contrarrestan estos problemas, ya que el nombre sugiere observar cambios en el DOM y proporcionarle un informe de todos los cambios que tuvieron lugar desde el inicio del cambio. Esta es una situación mucho mejor, ya que permite que los navegadores le notifiquen en un momento que tenga sentido, por ejemplo, cuando el documento está inactivo y todos los demás JavaScript que podrían hacer más cambios han terminado de ejecutarse o antes de que el navegador reinicie el navegador. recalc/repaint cycle, para que pueda aplicar cualquier cambio que realice, sin tener que repetir el ciclo poco después.

También le facilita la administración, ya que puede escanear todos los elementos modificados para encontrar lo que está buscando, en lugar de escribir muchos códigos de manejo de casos para las cosas que no le interesan, como era la situación con los Eventos de Mutación. Y lo más importante, solo va a llamarlo una vez, por lo que no tiene que preocuparse de que los cambios vayan a afectar a los elementos, es decir, ya no cambian de estado, sino que han cambiado.

Respondiendo a su pregunta, los observadores de mutación DOM son más lentos porque esperaron a que jQuery terminara su manipulación del DOM antes de que le notificara qué jQuery cambió. Que por la razón explicada anteriormente y su ejemplo, demuestra que es una solución más segura y eficiente (ya no causa un error), y realmente no le importaba que jQuery añadiera algo al DOM porque lo habría eliminado poco después. Con los observadores, usted habría recibido un informe detallando el elemento jQuery que se agrega y elimina.

Esto todavía es un poco problemático, sin embargo, porque tiene que averiguar qué sucedió realmente al hacer coincidir los elementos con todos los cambios que tuvieron lugar. La realidad es que, en lo que a ti respecta, no pasó nada (se agregó y eliminó el mismo elemento), así que nada ha cambiado realmente en la estructura del DOM. Para ayudar con esto hay una pequeña biblioteca llamada MutationSummary:

http://code.google.com/p/mutation-summary/

que calcula el efecto neto de los cambios y sólo llama a su devolución de llamada que pasa en esos cambios. Entonces, en su caso, su devolución de llamada no se habría llamado en absoluto, porque el efecto neto del cambio fue cero.

E.g. para lo siguiente, solo obtendrá un cambio. El estilo del cuerpo se cambió a la izquierda: 1000px. Aunque lo cambié en 1000 incrementos. El efecto neto del cambio es solo la diferencia entre su valor inicial y su valor final.

function moveBody() { 
    for (var i = 0; i < 1000; i++) document.body.style.left = i + 'px'; 
} 
moveBody(); 
+0

Muchas gracias @AshHeskes y buena explicación! Finalmente entiendo sus principios de ejecución. –

6

La respuesta simple es que los observadores de mutación son asincrónicos. Se envían cada vez que se siente el motor, un tiempo después del cambio y, en algunos casos, después de que se han producido muchos cambios. Eso puede ser mucho después de que un oyente de un evento de mutación de DOM lo haya notificado, pero mientras tanto, el motor ha sido libre de realizar su trabajo sin tener que crear y generar eventos constantemente para cada cambio de DOM insignificante.

+0

Gracias Nartowicz! –

Cuestiones relacionadas