2012-07-11 15 views
8

Il seguente codice utilizza DOM Mutation Event DOMNodeInserted per rilevare l'esistenza dell'elemento body e avvolgere il suo innerHTML in un wrapper.Gli osservatori di mutazione DOM sono più lenti degli eventi di mutazione 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> 

E nonostante il successo del wrapping, c'è un errore indica che un nodo non è stato trovato. This answer di una domanda ha spiegato che è perché quando jQuery è stato caricato, ha aggiunto un elemento div nel corpo per fare alcuni test, ma non è riuscito a rimuovere quell'elemento div perché quell'elemento è stato inserito nel wrapper in modo che non sia un elemento infantile del corpo più.

È possibile che questo esperimento ci dice che DOMNodeInserted evento è più veloce rispetto ai test di jQuery, perché elemento di prova di jQuery (div) ottenuto avvolto prima che possa essere rimosso da jQuery.




Ora il seguente codice può ottenere lo stesso manipolazione, e che sta utilizzando gli Osservatori DOM mutazione di nuova introduzione. A partire da questo momento (2012-07-11), funziona solo su Chrome 18 e versioni successive.

<!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> 

Questo codice non ha prodotto alcun errore. Ciò significa che jQuery è più veloce di DOM Mutation Observers, quindi è stato in grado di rimuovere il suo elemento di test (div) prima che quell'elemento possa essere inserito nel wrapper.




Da questi due esperimenti, troviamo che quando si tratta di velocità di esecuzione:

  • DOM mutazione Eventi > test di jQuery
  • test di jQuery > osservatori di mutazione DOM

Questo risultato può dimostrare in modo appropriato che DOM Mutation Observers è più lento di DOM Mutation Events?

risposta

24

Gli osservatori di mutazione DOM non devono essere più veloci degli eventi di mutazione DOM. Piuttosto sono destinati a essere più efficienti e più sicuri.

L'essenza di base della differenza è che DOM Mutation Events si attiva ogni volta che c'è un cambiamento. Così, per esempio, questo codice creerebbe un ciclo di richiamata, che alla fine danneggerebbe il browser.

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

Il fatto che essi sono chiamati in questo modo e così spesso ha anche un effetto significativo sul browser, in quanto costringe un interrupt tra i browser ricalcolare stile, reflow e riverniciare le forze di ciclo o peggio il browser di ricalcolare stili, reflow e ridisegno su ogni callback. Il problema è ulteriormente esasperato dal fatto che un altro codice potrebbe essere in esecuzione che apporta ulteriori modifiche al DOM, che continuerà ad essere interrotto dalla richiamata.

Inoltre, poiché gli eventi si propagano allo stesso modo dei normali eventi DOM, inizierai a sentire cambiamenti su elementi di cui potresti non interessarti o che non hanno tenuto conto nel tuo codice. Quindi l'intero meccanismo degli eventi mutazione DOM può diventare problematico da gestire abbastanza rapidamente.

Gli osservatori di mutazione DOM contrastano questi problemi, come suggerisce il nome osservando le modifiche al DOM e fornendo un rapporto di tutti i cambiamenti che hanno avuto luogo dall'inizio della modifica. Questa è una situazione molto migliore in quanto consente ai browser di notificarti in un momento che ha senso, ad esempio quando il documento è inattivo e tutti gli altri JavaScript che potrebbero rendere successive ulteriori modifiche o prima che il browser riavvii il ricalcola/ridisegna ciclo, in modo che possa applicare tutte le modifiche apportate, senza dover ripetere il ciclo poco dopo.

Inoltre, rende più facile la gestione, perché è possibile scansionare tutti gli elementi modificati per trovare ciò che si sta cercando, invece di scrivere un sacco di codice di gestione dei casi per cose che non ti interessano, come era la situazione con Mutation Events. E, ancora più importante, la chiamerà solo una volta, quindi non devi preoccuparti che eventuali ulteriori modifiche abbiano effetto sugli elementi, cioè che non siano più in cambiamento di stato, che siano cambiati.

Quindi, in risposta alla tua domanda, i DOM Mutation Observers sono più lenti perché hanno aspettato che jQuery finisse la sua manipolazione del DOM prima che ti notificasse cosa era cambiato jQuery. Che per la ragione spiegata sopra e il tuo esempio, dimostra che è una soluzione più sicura ed efficiente (non si causa più un errore), e non ti importava che jQuery aggiungesse qualcosa al DOM perché lo avrebbe rimosso poco dopo. Con Observers avresti ricevuto un rapporto che dettagliava l'elemento jQuery che veniva aggiunto e rimosso.

Questo è ancora un po 'problematico, tuttavia, perché è necessario capire cosa è effettivamente accaduto abbinando gli elementi con tutte le modifiche che hanno avuto luogo. La realtà è che per quanto ti riguarda non è successo niente (lo stesso elemento è stato aggiunto e rimosso) quindi nulla è realmente cambiato nella struttura del DOM. Per contribuire a questo c'è un po 'libreria chiamata MutationSummary:

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

che calcola l'effetto netto delle variazioni e chiama solo il callback passando tali modifiche. Quindi nel tuo caso il callback non sarebbe stato chiamato affatto, perché l'effetto netto del cambiamento era zero.

E.g. per quanto segue avrai solo una modifica. Lo stile del corpo è stato modificato a sinistra: 1000 px. Anche se l'ho cambiato in 1000 incrementi. L'effetto netto del cambiamento è solo la differenza tra il suo valore iniziale e quello finale.

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

Grazie mille @AshHeskes e bella spiegazione! Finalmente capisco i loro principi di esecuzione. –

6

La risposta semplice è che gli osservatori di mutazione sono asincroni. Vengono inviati ogni volta che il motore si sente come, qualche tempo dopo che il cambiamento è avvenuto e in alcuni casi dopo che sono avvenuti molti cambiamenti. Potrebbe passare molto tempo dopo che un listener di eventi mutazionali DOM ti avrebbe notificato, ma nel frattempo il motore è stato libero di portare a termine il proprio lavoro senza dover creare e creare costantemente eventi per ogni modifica DOM.

+0

Grazie Nartowicz! –