Se un elemento DOM viene rimosso, anche i suoi listener vengono rimossi dalla memoria?


Risposte:


307

Browser moderni

JavaScript semplice

Se un elemento DOM che viene rimosso è privo di riferimenti (nessun riferimento che lo punta), allora : l'elemento stesso viene raccolto dal Garbage Collector e da eventuali gestori / ascoltatori di eventi associati ad esso.

var a = document.createElement('div');
var b = document.createElement('p');
// Add event listeners to b etc...
a.appendChild(b);
a.removeChild(b);
b = null; 
// A reference to 'b' no longer exists 
// Therefore the element and any event listeners attached to it are removed.

Però; se ci sono riferimenti che puntano ancora a detto elemento, l'elemento e i suoi listener di eventi vengono mantenuti in memoria.

var a = document.createElement('div');
var b = document.createElement('p'); 
// Add event listeners to b etc...
a.appendChild(b);
a.removeChild(b); 
// A reference to 'b' still exists 
// Therefore the element and any associated event listeners are still retained.

jQuery

Sarebbe giusto presumere che i metodi pertinenti in jQuery (come remove()) funzionerebbero esattamente allo stesso modo (considerando che è remove()stato scritto usando removeChild()ad esempio).

Tuttavia, questo non è vero ; la libreria jQuery in realtà ha un metodo interno (che non è documentato e in teoria potrebbe essere modificato in qualsiasi momento) chiamato cleanData() (ecco come appare questo metodo ) che pulisce automaticamente tutti i dati / eventi associati a un elemento dopo la rimozione dal DOM (essere questo via. remove(), empty(), html("")ecc).


Browser meno recenti

I browser più vecchi, in particolare le versioni precedenti di IE, sono noti per avere problemi di perdita di memoria a causa del fatto che i listener di eventi mantengono i riferimenti agli elementi a cui erano collegati.

Se desideri una spiegazione più approfondita delle cause, dei motivi e delle soluzioni utilizzate per correggere le perdite di memoria della versione di IE legacy, ti consiglio vivamente di leggere questo articolo MSDN su Comprensione e risoluzione dei modelli di perdite di Internet Explorer.

Alcuni altri articoli rilevanti per questo:

Rimuovere manualmente gli ascoltatori sarebbe probabilmente una buona abitudine da prendere in questo caso (solo se la memoria è così vitale per la tua applicazione e stai effettivamente prendendo di mira tali browser).


22
Secondo la documentazione jquery quando si utilizza il metodo remove () su un elemento, tutti i listener di eventi vengono rimossi dalla memoria. Ciò influisce sull'elemento selezionato e su tutti i nodi figlio. Se si desidera mantenere in memoria i listener di eventi, utilizzare invece .detach (). Utile quando gli elementi rimossi verranno nuovamente inseriti sul dom.
Lothre1,

1
Se l'elemento contiene elem figlio, rimuoverà anche i listener di eventi su elementi figlio?
CBeTJlu4ok,

1
@ Lothre1: solo quando si utilizza il removemetodo il più delle volte il DOM verrà cancellato completamente. (come i collegamenti turbo o qualcosa del genere). Mi chiedo come viene influenzata la memoria se lo faccio document.body.innerHTML = ''...
vsync,

1
Avrei bisogno di qualcosa di più di "esperienza personale", più simile a dati concreti, test e collegamenti a specifiche che affermano come la memoria rimanga persistente su nodi che non sono più nel documento, questo è troppo importante per fidarsi della parola di qualcuno senza prove :)
vsync,

1
@ Lothre1 Grazie - Ho scavato un po 'più a fondo e ho scoperto come jQuery si comporta diversamente dal normale JavaScript in questo senso. Ho aggiornato la risposta.
dsgriffin,

23

per quanto riguarda jQuery:

il metodo .remove () estrae gli elementi dal DOM. Usa .remove () quando vuoi rimuovere l'elemento stesso, così come ogni cosa al suo interno. Oltre agli elementi stessi, vengono rimossi tutti gli eventi associati e i dati jQuery associati agli elementi. Per rimuovere gli elementi senza rimuovere dati ed eventi, utilizzare invece .detach ().

Riferimento: http://api.jquery.com/remove/

.remove()codice sorgente jQuery v1.8.2 :

remove: function( selector, keepData ) {
    var elem,
        i = 0;

    for ( ; (elem = this[i]) != null; i++ ) {
        if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
            if ( !keepData && elem.nodeType === 1 ) {
                jQuery.cleanData( elem.getElementsByTagName("*") );
                jQuery.cleanData( [ elem ] );
            }

            if ( elem.parentNode ) {
                elem.parentNode.removeChild( elem );
            }
        }
    }

    return this;
}

apparentemente usa jQuery node.removeChild()

Secondo questo: https://developer.mozilla.org/en-US/docs/DOM/Node.removeChild ,

The removed child node still exists in memory, but is no longer part of the DOM. You may reuse the removed node later in your code, via the oldChild object reference.

cioè i listener di eventi potrebbero essere rimossi, ma nodeesiste ancora in memoria.


3
Stai solo aggiungendo confusione: jQuery non fa nulla che con gestori così semplici removeChildnon farebbero. Entrambi ti restituiscono anche un riferimento che potresti continuare a ricollegare (in questo caso ovviamente rimane in memoria) o a lanciarlo (nel qual caso viene infine raccolto da GC e rimosso).
Oleg V. Volkov,

lo so: D. quindi dove sei colui che ha modificato la domanda? cos avrei potuto giurare che c'era qualcosa sull'uso di jquery per rimuovere un elemento DOM, nella domanda precedente. ora la mia risposta suona come se stessi spiegando le cose solo per accarezzare il mio ego. ehi puoi sempre
votare

8

Non esitate a guardare heap per vedere perdite di memoria nei gestori di eventi mantenendo un riferimento all'elemento con una chiusura e l'elemento mantenendo un riferimento al gestore di eventi.

Il Garbage Collector non ama i riferimenti circolari.

Caso di perdita di memoria usuale: ammettere che un oggetto ha un riferimento a un elemento. Quell'elemento ha un riferimento al gestore. E il gestore ha un riferimento all'oggetto. L'oggetto ha riferimenti a molti altri oggetti. Questo oggetto faceva parte di una collezione che ritieni di aver gettato via senza fare riferimento alla tua collezione. => l'intero oggetto e tutto ciò a cui rimarrà rimarrà in memoria fino all'uscita dalla pagina. => devi pensare a un metodo di uccisione completo per la tua classe di oggetti o, ad esempio, fidarti di un framework mvc.

Inoltre, non esitare a utilizzare la parte della struttura di conservazione degli strumenti di sviluppo di Chrome.


8

Basta estendere altre risposte ...

I gestori di eventi delegati non verranno rimossi dopo la rimozione dell'elemento.

$('body').on('click', '#someEl', function (event){
  console.log(event);
});

$('#someEL').remove(); // removing the element from DOM

Ora controlla:

$._data(document.body, 'events');

9
Il gestore eventi è collegato al corpo e non #someEl, naturalmente il gestore non deve essere rimosso fintanto che il corpo è ancora qui.
Yangshun Tay,

Questo può essere rimosso manualmente: stackoverflow.com/questions/22400907/…
fangxing

7

Per quanto riguarda jQuery, i seguenti metodi comuni rimuoveranno anche altri costrutti come i gestori di dati e eventi:

rimuovere()

Oltre agli elementi stessi, vengono rimossi tutti gli eventi associati e i dati jQuery associati agli elementi.

vuoto()

Per evitare perdite di memoria, jQuery rimuove altri costrutti come i gestori di dati e eventi dagli elementi figlio prima di rimuovere gli elementi stessi.

html ()

Inoltre, jQuery rimuove altri costrutti come i gestori di dati e eventi dagli elementi figlio prima di sostituirli con il nuovo contenuto.


2

Sì, anche il Garbage Collector li rimuoverà. Potrebbe non essere sempre il caso dei browser legacy.


7
la tua dichiarazione dovrebbe essere supportata da documentazione API, esempi, ecc.
Paolo Fulgoni,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.