Ich möchte MutationObserver
feststellen, ob ein bestimmtes HTML-Element irgendwo auf einer HTML-Seite hinzugefügt wird. Zum Beispiel möchte ich sagen, dass ich erkennen möchte, ob <li>
irgendwo im DOM irgendwelche hinzugefügt wurden.
Alle MutationObserver
Beispiele, die ich bisher gesehen habe, erkennen nur, ob einem bestimmten Container ein Knoten hinzugefügt wurde. Zum Beispiel:
etwas HTML
<body>
...
<ul id='my-list'></ul>
...
</body>
MutationObserver
Definition
var container = document.querySelector('ul#my-list');
var observer = new MutationObserver(function(mutations){
// Do something here
});
observer.observe(container, {
childList: true,
attributes: true,
characterData: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});
In diesem Beispiel wird das MutationObserver
Setup so eingerichtet, dass ein ganz bestimmter Container ( ul#my-list
) überwacht wird, um festzustellen, ob irgendwelche <li>
an ihn angehängt sind.
Ist es ein Problem, wenn ich weniger spezifisch sein und <li>
über den gesamten HTML-Text wie folgt achten möchte:
var container = document.querySelector('body');
Ich weiß, dass es in den grundlegenden Beispielen funktioniert, die ich für mich selbst eingerichtet habe ... Aber ist es nicht ratsam, dies zu tun? Wird dies zu einer schlechten Leistung führen? Und wenn ja, wie würde ich dieses Leistungsproblem erkennen und messen?
Ich dachte, es gibt vielleicht einen Grund, warum alle MutationObserver
Beispiele mit ihrem Zielcontainer so spezifisch sind ... aber ich bin mir nicht sicher.
quelle
MutationObserver
s verwendet und sie rekursiv das gesamte DOM beobachten lassen. Ich persönlich hatte noch nie ein Problem mit der Leistung.subtree: true
für große Dokumente und es war nie ein Problem.li
Elementen beobachten? Wenn etwas ein Kandidat für eine nicht optimale Leistung ist, würde ich sagen, dass Sie nach mehr Ereignissen fragen, als Sie benötigen.MutationObserver
Bibliothek zur Überwachung der Webleistung, ein Gesamtdokument, um die Ladezeit von SPAs-Seiten zu messen.Antworten:
Diese Antwort gilt hauptsächlich für große und komplexe Seiten.
Wenn ein nicht optimierter MutationObserver-Rückruf vor dem Laden / Rendern der Seite angehängt wird, kann die Ladezeit der Seite (z. B. 5 bis 7 Sekunden) um einige Sekunden verlängert werden, wenn die Seite groß und komplex ist ( 1 , 2 ). Der Rückruf wird als Mikrotask ausgeführt, der die weitere Verarbeitung von DOM blockiert und auf einer komplexen Seite hunderte oder tausendmal pro Sekunde ausgelöst werden kann. Die meisten Beispiele und vorhandenen Bibliotheken berücksichtigen solche Szenarien nicht und bieten gut aussehenden, benutzerfreundlichen, aber möglicherweise langsamen JS-Code.
Verwenden Sie immer den devtools-Profiler und versuchen Sie, Ihren Beobachter-Rückruf weniger als 1% der gesamten CPU-Zeit zu verbrauchen, die beim Laden der Seite verbraucht wird.
Vermeiden Sie das Auslösen eines erzwungenen synchronen Layouts, indem Sie auf offsetTop und ähnliche Eigenschaften zugreifen
Vermeiden Sie die Verwendung komplexer DOM-Frameworks / -Bibliotheken wie jQuery und bevorzugen Sie native DOM-Inhalte
Verwenden Sie beim Beobachten von Attributen die
attributeFilter: ['attr1', 'attr2']
Option in.observe()
.Beobachten Sie nach Möglichkeit direkte Eltern nicht rekursiv (
subtree: false
).Zum Beispiel ist es sinnvoll, durch
document
rekursives Beobachten auf das übergeordnete Element zu warten , den Beobachter bei Erfolg zu trennen und diesem Containerelement ein neues nicht rekursives Element beizufügen.Wenn Sie auf nur ein Element mit einem
id
Attribut warten , verwenden Sie das wahnsinnig schnelle Element ,getElementById
anstatt dasmutations
Array aufzulisten (es kann Tausende von Einträgen enthalten): Beispiel .Falls das gewünschte Element auf der Seite relativ selten ist (z. B.
iframe
oderobject
), verwenden Sie die vongetElementsByTagName
und zurückgegebene Live-HTMLCollectiongetElementsByClassName
und überprüfen Sie sie alle erneut, anstatt beispielsweise aufzuzählen,mutations
ob sie mehr als 100 Elemente enthält.Vermeiden Sie die Verwendung
querySelector
und vor allem die extrem langsamenquerySelectorAll
.Wenn dies
querySelectorAll
innerhalb des MutationObserver-Rückrufs absolut unvermeidbar ist, führen Sie zuerst einequerySelector
Überprüfung durch und fahren Sie bei Erfolg mit fortquerySelectorAll
. Im Durchschnitt wird eine solche Kombination viel schneller sein.Wenn Sie auf Chrome / ium vor 2018 abzielen, verwenden Sie nicht die integrierten Array-Methoden wie forEach, filter usw., für die Rückrufe erforderlich sind, da das Aufrufen dieser Funktionen in Chrome V8 im Vergleich zur klassischen
for (var i=0 ....)
Schleife (10-100) immer teuer war mal langsamer), und der Rückruf von MutationObserver kann Tausende von Knoten auf komplexen modernen Seiten melden.Wenn Sie auf Browser vor 2019 abzielen, verwenden Sie die langsamen ES2015-Schleifen nicht wie
for (let v of something)
im MutationObserver-Rückruf, es sei denn, Sie transpilieren, damit der resultierende Code so schnell wie die klassischefor
Schleife ausgeführt wird.Wenn das Ziel darin besteht, das Erscheinungsbild der Seite zu ändern und Sie zuverlässig und schnell feststellen können, dass sich die hinzugefügten Elemente außerhalb des sichtbaren Teils der Seite befinden, trennen Sie den Beobachter vom Computer und planen Sie eine Überprüfung und Wiederaufbereitung der gesamten Seite über
setTimeout(fn, 0)
: Sie wird ausgeführt, wenn die Der erste Ausbruch der Analyse- / Layout-Aktivität ist beendet und der Motor kann "atmen", was sogar eine Sekunde dauern kann. Dann können Sie die Seite beispielsweise mit requestAnimationFrame unauffällig in Blöcken verarbeiten.Verwenden Sie Debounce oder eine ähnliche Technik, z. B. akkumulieren Sie Mutationen in einem äußeren Array und planen Sie einen Lauf über setTimeout / requestIdleCallback / requestAnimationFrame:
const queue = []; const mo = new MutationObserver(mutations => { if (!queue.length) requestAnimationFrame(process); queue.push(mutations); }); function process() { for (const mutations of queue) { // .......... } queue.length = 0; }
Zurück zur Frage:
Da
li
ein direktes Kind ist, und wir suchen für zusätzliche Knoten, benötigt die einzige Option istchildList: true
(Beratung # 2 siehe oben).new MutationObserver(function(mutations, observer) { // Do something here // Stop observing if needed: observer.disconnect(); }).observe(document.querySelector('ul#my-list'), {childList: true});
quelle
for... of
ist sie jetzt in V8 so schnell wie eine reguläre for-Schleife:]