effizienteste Methode zum iterieren über alle DOM-Elemente?
Leider brauche ich für die Iteration über die DOM-Elemente einer Seite und ich Frage mich, was die effizienteste Technik ist. Ich könnte wahrscheinlich benchmark diese selber und könnte, wenn ich die Zeit habe, aber ich bin der Hoffnung, jemand hat bereits erlebt, oder hat einige Optionen, die ich hatte nicht darüber nachgedacht.
derzeit bin ich mit jQuery und dies zu tun:
$('body *').each(function(){
var $this = $(this);
//do stuff
});
Während es funktioniert, Es zu verursachen scheint einigen Verzögerungen auf dem client. Es könnte auch optimiert werden mit mehr spezifischen jQuery Kontext wie $('body', '*')
Es fiel mir ein, dass native javascript ist in der Regel schneller als jQuery und ich fand diese.
var items = document.getElementsByTagName("*");
for (var i = 0; i < items.length; i++) {
//do stuff
}
Ich gehe davon aus, dass die native option ist schneller. Frage mich, ob es gibt andere Optionen, die ich hatte nicht darüber nachgedacht. Vielleicht eine rekursive option, iteriert über die Kind-Knoten parallel.
- Ja, die Vanille-DOM Weg schneller ist. Aber warum müssen Sie die Iteration über alle Elemente?
- cache der Elemente.Länge, so bist du nicht berechnend es jede iteration der Schleife, aber ja, die for-Schleife mit DOM-aufrufen werden, schneller als .jeder
- Es sollte nicht sein, die einen spürbaren performance-Unterschied. jQuery verwenden
querySelectorAll
oder etwas, vorausgesetzt, dass der zur Verfügung. Einige subselector Situationen könnte es nicht verwenden, um nicht-native Methoden, wenn Sie können, aber ich glaube nicht, dass dies ist einer von Ihnen, und als Sie festgestellt haben, können Sie code, um sicherzustellen, dass es ohnehin tut. Zwar gibt es einige zusätzlichen Aufwand, es wird nicht materielles im Vergleich zu dem, was Sie bereits tut (die Schleife durch jedes element) in beiden Situationen. Ich würde stick mit jQuery, es sei denn, Sie wirklich nicht egal, wenn es funktioniert in älteren IE-Browser. element.getElementsByTagName('*')
funktioniert im IE6, die alle von jQuery unterstützt, so weit zurück als gut.- Aus Interesse, warum braucht man es überhaupt zu tun?
- jeden Tag etwas neues lernen, für einige Grund, warum ich dachte, es war gebrochen IE6. Aber sieht aus wie du Recht hast.
- Auch jQuery kann nicht optimieren "body *" sehr gut. Er tut große mit nur "Körper", sondern Sie verwenden Sie die "Körper *" es nutzt brutzeln JS. Das heißt, er will schließlich am Ende aufrufen
document.querySelectorAll('body *')
, aber es hat über 200 Zeilen Javascript-code zuerst, bevor Sie entscheidet zu tun, inklusive einer regex-test und andere Dinge. Das könnte nicht wie viel scheinen, aber es Vergleich zudocument.body.getElementsByTagName('*')
es eine Menge. - Bezüglich warum ich muss. Ich habe einen Fall, wo ich zu finden alle Elemente auf einer Seite, die die css-Attribut position:fixed und entsprechend zu handeln. Alles, was ich gefunden in Bezug auf diese wies bei der Iteration über jedes element. Ich könnte wahrscheinlich haben die Frage angesprochen, in Bezug auf oder stellen Sie eine eigene Frage für, die.
- Eigentlich "body *" ist schneller (überrascht mich auch): jsperf.com/js-vs-jquery-select-all .. auch interessant, dass es einen Unterschied von etwa einer Größenordnung von 2 zwischen jQuery und die native Methode. Dies ist wahrscheinlich, weil jQuery durchläuft die gesamte Liste zu erstellen, die das Ergebnis intern festgelegt, und dann versuchen wir es noch einmal danach. In diesem Sinne, die DOM-Methode wäre schneller. Aber das hat zu tun mit ResultSet Gebäude und nicht brutzeln.
- ist das wirklich alles, was Sie haben, mit zu arbeiten, die für dieses problem? Sie weiß nicht: alle Container, die die "festen" Dinge sein könnten, innerhalb, alle element-Typen (z.B. die wahrscheinlich nur
div
Elemente); Sie haben keine Möglichkeit zum zuweisen von Klassen oder sonst markieren Sie die Dinge, die Sie suchen, für andere als für einen Stil? Ist das ein real-Welt-situation oder ein hypothetisches problem? - Ich glaube, der Grund, warum Sie gefunden jQuery schneller sein wird da Sie
document.querySelectorAll('*')
stattdocument.querySelectorAll('body *')
oderdocument.body.querySelectorAll('*')
. Ich aktualisiert Ihre tests entsprechend: jsperf.com/js-vs-jquery-select-all/2 - Ich glaube, Sie finden
document.body.getElementsByTagName('*')
ist die Schnellste. Ich bin mir nicht sicher, wie Sie Sie jsperf funktioniert zwar, aber ich glaube, da gibt es keinen body-tag in Ihre eingefügte HTML gibt es keine Treffer zu "body *", denn ich bin mir ziemlich sicher, dass es läuft im Fenster-Objekt. - du hast Recht (mein test war nicht fair), aber es war gebrochen, in der Gunst der Javascript-code. Ihre version verengt sich die Kluft. Es ist ein body-tag für die tests, die in der Beschreibung heißt es, die Inhalte werden in den Körper ein gültiges HTML5-Dokument. Aber ich denke, was die meisten wichtig zu beachten ist der Unterschied zwischen den verschiedenen jQuery Wege gehen, die Auswahl (und die js) - es ist ziemlich negligble, so cleary keiner von Ihnen ist mit brutzeln. Wenn das Zischen war invovled es wäre ein Faktor von mindestens 10 würde ich denken.
- Ah, ich sehe, dass es eingefügt ist in ein body-tag jetzt. 🙂
- Ich Schreibe an einem 3rd-party-Bibliothek enthalten ist, in die Benutzer-Seiten. Ich habe keine Ahnung, was Ihre Entwürfe oder markup Aussehen. Unsere Bibliothek injiziert einem festen element (einer Kopfzeile), die dazu führen können, überlappen sich mit Ihren Entwürfen. Um dies zu verhindern, bewegen wir uns alle von Ihrer position:fixed Elemente entsprechend. Die einzigen Dinge, die ich kann davon ausgehen, dass Sie vielleicht haben position:fixed Elemente innerhalb des body-Elements.
- Es nutzt brutzeln obwohl (für die
$('body *')
Fall). Ich verwendet die unkomprimierte jQuery-Quelle mit einem Haltepunkt in der init-Funktion und dann lief es und fand, dass Sie ging in das Zischen, aber Sizzle ist geändert von jQuery, wennif ( document.querySelectorAll )
, so dass am Ende es endet nur mitcontext.querySelectorAll(query)
(wo Kontext - = = = - Dokument und query = = = " Körper -*'). - Was ich meine, ist es nicht wirklich nutzen brutzeln, die zum Durchlaufen des dom. Sicher - es ist mehr Aufwand - eine Menge von Zeilen von code in jQuery, das gleiche zu tun. Aber das ist nichts im Vergleich zu der Zeit, die Sie verbringen, Durchlaufen die Liste der Elemente und die überprüfung Klassen, das ist, was ich damit meine ist, vernachlässigbar.
- elementFromPoint helfen könnte. Wenn es sicher davon ausgehen, dass die fixed-position-element werden die höchsten z-index (wenn nicht, gut, das ist einfach komisch), und Sie können ein paar andere grundlegende Annahmen über eine minimale Größe der festen element (e.g, es ist mindestens 10 Pixel hoch und 50 Pixel breit) könnte man leicht in einer Schleife durch ein Gitter in den Bereich, den Sie beabsichtigen zu besetzen, und aktivieren Sie nur die Stile, die die einzigartigen Elemente, die Sie finden unter den einzelnen Punkten in diesem Gitter. Sie würden laufen, eine native JS-Methode nicht mehr als (ich glaube) ein paar Dutzend mal, wahrscheinlich viel schneller als die Auswahl esp (mit IE6)
- Mögliche Duplikate von: Wie eine Schleife durch ALLE DOM-Elemente auf einer Seite?
- überprüfen Sie das benchmark jsben.ch/#/Ro9H6
Du musst angemeldet sein, um einen Kommentar abzugeben.
Vanilla Javascript Weg, den Sie geschrieben ist der Schnellste. Es wird schneller sein als die jQuery-Lösung, die Sie gepostet hat (Siehe mein Kommentar auf die Frage). Wenn Sie nicht entfernen oder hinzufügen von etwas, um den DOM in Ihre Schleife und die Reihenfolge der traversal egal, Sie können auch beschleunigen immer so leicht durch Durchlaufen in umgekehrter Richtung:
Bearbeiten: aktivieren Sie dieses benchmark um zu sehen, wie viel Zeit Sie sparen können, indem Sie mit dem native-code: http://jsben.ch/#/Ro9H6
i--
ist der bedingungsteil der Schleife. Es ist wie gesagti-- != 0
, aber da0
istfalsy
Sie verlassen kann, dass ein Teil ausi++
bei jeder iteration und dann nicht mit dem Ergebnis, dass die expression und stattdessen separat auswerteni < length
für die Schleife Bedingung, die Sie nutzen könnteni--;
(effektivi-- != 0
), die beide für den Fortschritt der iteration und der Schleife Zustand.UPDATE:
Nicht verwenden
$('body *')
zur Iteration über die Elemente. Es wird viel schneller zu verwenden$('*')
wenn Sie sich für das JQuery-Methode (siehe Hinweise für weitere details).Plain ol' JavaScript ist viel schneller, relativ gesehen.
Mit einem test fiddle, ich bekomme etwa 30 MS verarbeiten 13000 Elemente mit JQuery, und 8ms zu verarbeiten 23000 Elemente mit JavaScript (beide getestet in Chrome):
Hinweis:, es sei denn, Sie haben eine unglaublich große Menge von Elementen auf der Seite, das ist nicht zu viel Unterschied. Auch, sollten Sie sich vielleicht mal die Logik in deinem loop, wie das könnte der limitierende Faktor sein.
Update:
Hier ist die aktualisierte Ergebnisse bei der Betrachtung viel mehr Elemente (etwa 6500 pro loop), die ich bekomme, 648000 Elemente in 1500ms mit JQuery, und 658000 Elemente in 170ms mit JavaScript. (beide getestet in Chrome):
Sieht aus wie JavaScript beschleunigt, während JQuery blieb in etwa gleich.
var $this = $(this);
. Die beiden Iteratoren identisch und ich denke, Sie werden herausfinden, dass dieser Unterschied weitgehend verschwindet.var $this = $(this);
in irgendeiner form ist ein erforderlicher Teil des JQuery-test. Ich bin offen für Vorschläge über verschiedene Möglichkeiten, sich ein handle auf das Objekt, obwohl.querySelectorAll('body *')
sind Sie fast identisch wieder. Ich bin mir ziemlich sicher, dass das die einzige Methode ist, die jQuery nutzt, das erklärt vermutlich den großen Unterschied gesehen in deinem letzten Beispiel. Das ist interessant, denn ich hätte nicht gedacht, dass sich mit dem element-gezielte Methode, wie Sie es getan hat (body.getElements...
) wäre so viel schneller alsdocument.querySelectorAll
(zumindest in chrome habe ich dieses). Es ist natürlich etwas akademisch, da man nicht normal laufen die gleichen Selektor tausend mal hintereinander 🙂Ist es nicht eine gute Idee im Allgemeinen, aber dieser sollte funktionieren:
Nicht inklusive textnodes:
Bearbeitet!
firstElementChild
undnextElementSibling
statt. Ich glaube nicht, das wird inhaltlich anders als mit einem Selektor sobald Sie fügen Sie den rest der Logik (z.B. das ist nicht das, was es langsam).Der Schnellste Weg zu sein scheint
document.all
(beachten Sie, dass es ist eine Eigenschaft, keine Methode).Habe ich geändert, die fiddle von Briguy Antwort, melden Sie diesen anstelle von jQuery, und es ist durchweg schneller (als
document.getElementsByTagName('*')
).Die Geige.
Ist dies eine Lösung für das problem, wie beschrieben in die Kommentare (wenn auch nicht die eigentliche Frage). Ich denke, es wäre viel schneller, die Nutzung
elementFromPoint
testen Sie den Bereich, wo Sie wollen, um Ihre ortsfesten element, und sorgen sich nur um Elemente in diesem Bereich. Ein Beispiel ist hier:http://jsfiddle.net/pQgwE/4/
Grundsätzlich, nur einige minimal mögliche Größe eines Elements, die Sie suchen, und Scannen Sie die gesamte Fläche, die Ihre Feste position-element besetzen will. Erstellen Sie eine Liste der einzigartigen Elemente dort gefunden, und sorgen sich nur um die überprüfung der Stil dieser Elemente.
Beachten Sie, dass diese Technik geht davon aus, dass das element, das Sie suchen, hat den höchsten z-index (das scheint eine vernünftige Annahme für die Feste position). Wenn dies nicht gut genug ist, dann könnte dies angepasst werden, ausblenden (oder weisen Sie einen minimalen z-index), um jedes element, nachdem es entdeckt wurde, und testen Sie den Punkt wieder, bis nichts mehr gefunden wird (um sicher zu sein), und dann wiederherstellen Sie Sie anschließend. Dies sollte geschehen, so schnell, wie das sein nicht wahrnehmbar.
HTML:
JS: