2009-03-24 13 views
27

Estoy tratando de descubrir la forma más efectiva de clonar profundamente un árbol DOM dentro del navegador.Clonación profunda vs configuración de innerHTML: ¿qué es más rápido?

Si comienzo con

var div = document.getElementById("source"); 
var markup = div.innerHTML; 

¿Qué va a ser más rápido,

var target = div.cloneNode(true); 

o

var target = document.cloneNode(false); 
target.innerHTML = markup; 

entiendo la plataforma de navegador puede hacer una gran diferencia en este caso, por lo que cualquier la información sobre cómo se comparan en el mundo real sería apreciada.

+0

No obtengo lo que estás buscando. ¿Quieres clonar un elemento? 'var target = div.cloneNode (true);' hizo exactamente eso. ¿Qué tiene que ver la clonación con 'innerHTML'? –

+0

Los resultados específicos dependerán de su navegador, pero encontré dos pruebas relevantes en jsPerf [aquí] (http://jsperf.com/clonenode-vs-createelement-performance/39) y [aquí] (http: // jsperf .com/innerclone/13). – Blazemonger

+0

[innerHTML destruye los detectores de eventos] (http://stackoverflow.com/q/595808/1048572), no lo use. – Bergi

Respuesta

48

¡Vamos a probar!

I añade el siguiente código a una copia de la página Preguntas de StackOverflow (eliminación de secuencias de comandos existentes en primer lugar, y en funcionamiento desde cero con una de las timeit() s sin comentar cada vez, tres carreras de 100 ops:

function timeit(f) { 
    var start= new Date(); 
    for (var i=100; i-->0;) { 
     f(); 
    } 
    return new Date()-start; 
} 

var c= document.getElementById('content'); 
var clones= []; 

//alert('cloneNode: '+timeit(function() { 
// clones.push(c.cloneNode(true)); 
//})) 

//alert('innerHTML: '+timeit(function() { 
// var d= document.createElement('div'); 
// d.innerHTML= c.innerHTML; 
// clones.push(d); 
//})) 

Éstos son los resultados ejecutando en un VirtualBox en un Core 2 Q9300:

IE7 
cloneNode: 3238, 3235, 3187 
innerHTML: 8442, 8468, 8552 

Firefox3 
cloneNode: 1294, 1315, 1289 
innerHTML: 3593, 3636, 3580 

Safari3 
cloneNode: 207, 273, 237 
innerHTML: 805, 818, 786 

Chrome1 
cloneNode: 329, 377, 426 
innerHTML: 2327, 2536, 2865 

Opera10 
cloneNode: 801, 791, 771 
innerHTML: 1852, 1732, 1672 

Así cloneNode (verdadero) es mucho más rápido que copiar innerHtml por supuesto que siempre iba a ser; serialising un DOM a texto y. luego volver a analizarlo desde HTML es un trabajo duro. En DOM, las operaciones secundarias suelen ser lentas porque las está insertando/moviendo una a una; las operaciones DOM a la vez como cloneNode no tienen que hacer eso.

Safari logra hacer el innerHTML op increíblemente rápido, pero aún no tan rápido como lo hace cloneNode. IE es, como se esperaba, un perro.

Por lo tanto, auto -1s a todos los que dijeron que innerHTML Obviamente sería más rápido sin tener en cuenta lo que la pregunta estaba haciendo en realidad.

Y sí, jQuery usa innerHTML para clonar. No porque es más rápido aunque - leer la fuente:

// IE copies events bound via attachEvent when 
// using cloneNode. Calling detachEvent on the 
// clone will also remove the events from the orignal 
// In order to get around this, we use innerHTML. 

jQuery utiliza Element.attachEvent() para implementar su propio sistema de eventos, por lo que, naturalmente, que necesita para evitar ese error. Si no lo necesita, puede evitar los gastos generales.

[fuera de tema a un lado: Por otra parte, creo que la celebración de jQuery como el pináculo de la mejor práctica puede ser un poco confundido, especialmente teniendo en cuenta la siguiente línea:

html.replace(/ jQuery\d+="(?:\d+|null)"/g, "") 

Así es - jQuery añade su propio atributos arbitrarios para los elementos HTML, y luego debe deshacerse de ellos cuando los clona (o de otra manera da acceso a su marcado, como a través del método $(). html()). Esto es bastante feo, pero luego piensa que la mejor manera de hacerlo es procesar HTML usando expresiones regulares, que es el tipo de error básico que esperarías más de los ingenuos cuestionarios SO de reputación que el autor de Second Coming Best JS Marco Evar.

Espero que no tenga la cadena "jQuery1 =" 2 "" en cualquier parte del contenido de su texto, porque de ser así, la perdió misteriosamente. Gracias jQuery! Así termina el fuera de tema a un lado.]

+0

Excelentes análisis. El único pequeño inconveniente es que estaba buscando serializar previamente la fuente innerHTML una vez, no todas las veces en el ciclo. Pero todavía resulta más lento. Gracias. – levik

+0

Gran respuesta Bobince. Solo me pregunto, ¿cuál * sería * la mejor manera de dejar estos atributos adicionales? ¿Buscar solo dentro de los corchetes angulares de una lista blanca de elementos HTML, o hacer un elemento DOM y luego usar jQuery's 'removeAttr()' en él? Tal vez sea una compensación, podría ser mucho más rápido a expensas de destruir a cualquiera que use esa cadena. Aunque es raro, debería haber algún tipo de advertencia en los documentos jQuery (no lo he encontrado si lo hay) – alex

+0

Lo que haría sería establecer una propiedad JS para el ID en lugar de un atributo HTML. IE tiene un error donde las propiedades se serializan como si fueran atributos, pero solo se activa cuando el valor de la propiedad es un tipo de datos primitivo. Las propiedades de tipo objeto no están incluidas en el 'innerHTML'. Por lo tanto, establecer 'elementnode.jQueryId45438 = [1]' (un objeto Array) permitiría a jQuery poner su identificación única en el elemento sin estropear la serialización y requiriendo cualquier reparación post-'innerHTML'. – bobince

1

Hmmm ... curiosamente, acabo de hacer una prueba en Firefox 3, y un clon profundo parece ser aproximadamente 3 veces más rápido que ir a la ruta innerHTML.

Estoy seguro de que innerHTML es aún más rápido que las operaciones DOM individuales, pero al menos para la clonación profunda, cloneNode (verdadero) parece estar mejor optimizado.

Cuestiones relacionadas