2009-03-24 4 views
27

Ich versuche herauszufinden, wie man einen DOM-Baum innerhalb des Browsers performant klonen kann.Deep Cloning vs Einstellung von innerHTML: Was ist schneller?

Wenn ich beginnen mit

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

Was wird schneller sein,

var target = div.cloneNode(true); 

oder

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

Ich verstehe die Browser-Plattform einen großen Unterschied hier machen kann, so dass jeder Informationen darüber, wie diese in der realen Welt vergleichbar sind, würden geschätzt werden.

+0

Ich verstehe nicht, wonach Sie suchen. Sie möchten ein Element klonen? 'var target = div.cloneNode (true);' tat genau das. Was hat das Klonen mit "innerHTML" zu tun? –

+0

Die spezifischen Ergebnisse hängen von Ihrem Browser ab, aber ich fand zwei relevante Tests bei jsPerf [hier] (http://jsperf.com/clonenode-vs-createelement-performance/39) und [hier] (http: // jsperf .com/innerklon/13). – Blazemonger

+0

[innerHTML zerstört Ereignis-Listener] (http://StackOverflow.com/q/595808/1048572), verwenden Sie es nicht. – Bergi

Antwort

48

Lass uns testen!

Ich habe den folgenden Code auf eine Kopie von Fragen Seite Stackoverflow (Entfernen von ersten vorhandenen Skripten und von Grund auf mit einem der timeit() s uncommented jedes Mal um, drei Läufe von 100 ops ausgeführt wird:

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); 
//})) 

Hier sind die Ergebnisse auf einem Core 2 Q9300 auf einem VirtualBox:

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 

So cloneNode (true) ist viel schneller als innerHTML- Kopieren natürlich war es immer sein werde, Serialisierung ein DOM in Text und. dann ist das erneute Analysieren von HTML eine harte Arbeit Bei DOM-Kind-Operationen ist es normalerweise langsam, dass Sie sie einzeln einfügen/verschieben; all-at-once-DOM-Operationen wie cloneNode müssen das nicht tun.

Safari schafft es, die innerHTML-Operation erstaunlich schnell, aber immer noch nicht annähernd so schnell wie es CloneNode zu tun. IE ist wie erwartet ein Hund.

Also, Auto-1s rund um alle, die sagten, innerHTML wäre Offensichtlich schneller, ohne darüber nachzudenken, was die Frage tatsächlich tat.

Und ja, jQuery verwendet innerHTML zum Klonen. Nicht, weil es zwar schneller ist - lesen Sie die Quelle:

// 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 verwendet Element.attachEvent() ein eigenes Event-System zu implementieren, so natürlich braucht es diesen Fehler zu vermeiden. Wenn Sie dies nicht tun müssen, können Sie den Overhead vermeiden.

[Off-Topic beiseite: Dann wieder, ich denke, jQuery up als Gipfel der Best Practice hält ein bisschen verwechselt werden können, vor allem die nächste Zeile gegeben:

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

Das ist richtig - jQuery fügt seine eigene beliebige Attribute zu HTML-Elementen und müssen sie dann loswerden, wenn sie sie klonen (oder anderweitig Zugriff auf ihr Markup geben, z. B. über die $() .html() -Methode). Das ist hässlich genug, aber dann denkt es, dass HTML am besten mit regulärem Ausdruck verarbeitet wird. Das ist die Art von Grundfehler, die man eher von naiven 1-Reputation-SO-Fragestellern erwartet als vom Autor des Second-Coming-Best-JS Rahmen Evar.

Ich hoffe, Sie hatten nicht die Zeichenfolge "jQuery1 =" 2 "" irgendwo in Ihrem Textinhalt, 'cos wenn Sie es auf mysteriöse Weise verloren haben. Danke jQuery! Damit endet das Off-Thema zur Seite.]

+0

Excellent analisys. Der einzige kleine Fehler ist, dass ich die Quelle innerHTML vorserialisieren wollte, nicht jedes Mal in der Schleife. Aber es stellt sich immer noch langsamer heraus. Danke. – levik

+0

Große Antwort Bobince. Ich frage mich nur, was * wäre * der beste Weg, um diese zusätzlichen Attribute fallen zu lassen? Suchen Sie nur in spitzen Klammern von einer Whitelist von HTML-Elementen oder machen Sie ein DOM-Element und verwenden Sie jQuerys 'removeAttr()' darauf? Vielleicht ist es ein Kompromiss, es könnte viel schneller gehen, wenn jemand, der diese Saite benutzt, beschädigt wird. Obwohl es selten ist, sollte es eine Art Warnung in den jQuery-Dokumenten geben (ich habe es nicht gefunden, wenn es da ist). – alex

+0

Was ich tun würde wäre, eine JS-Eigenschaft für die ID anstelle eines HTML-Attributs zu setzen. IE hat einen Fehler, bei dem Eigenschaften serialisiert werden, als wären sie Attribute, aber es tritt nur dort ein, wo der Eigenschaftswert ein primitiver Datentyp ist. Objekttypeigenschaften sind nicht in der 'innerHTML' enthalten. Das Setzen von 'elementnode.jQueryId45438 = [1]' (ein Array-Objekt) würde es jQuery erlauben, seine eindeutige ID auf das Element zu setzen, ohne die Serialisierung zu verkomplizieren und irgendwelche Post-'innerHTML'-Korrekturen erforderlich zu machen. – bobince

-5

Im Allgemeinen ist innerHTML schneller. Überprüfen Sie das heraus, ein benchmark across many platforms:

+0

Es ist nur nützlich, wenn Sie neue Elemente erstellen, anstatt sie zu klonen –

1

Hmmm ... interessanterweise habe ich gerade einen Test in Firefox 3, und ein tiefer Klon scheint etwa 3 mal schneller als die innereHTML Route.

Ich bin sicher, dass innerHTML immer noch schneller ist als einzelne DOM-Operationen, aber cloneNode (true) scheint zumindest für das tiefe Klonen besser optimiert zu sein.