Si un élément DOM est supprimé, ses écouteurs sont-ils également supprimés de la mémoire?

Réponses:

307

Navigateurs modernes

JavaScript simple

Si un élément DOM qui est supprimé est sans référence (aucune référence pointant vers lui), alors oui - l'élément lui-même est récupéré par le garbage collector ainsi que tous les gestionnaires d'événements / écouteurs qui lui sont associés.

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.

Toutefois; s'il existe des références qui pointent toujours vers ledit élément, l'élément et ses écouteurs d'événements sont conservés en mémoire.

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

Il serait juste de supposer que les méthodes pertinentes dans jQuery (comme remove()) fonctionneraient exactement de la même manière (considérant que cela a remove()été écrit en utilisant removeChild()par exemple).

Cependant, ce n'est pas vrai ; la bibliothèque jQuery a en fait une méthode interne (qui n'est pas documentée et qui peut en théorie être modifiée à tout moment) appelée cleanData() (voici à quoi ressemble cette méthode ) qui nettoie automatiquement toutes les données / événements associés à un élément lors de sa suppression du DOM que ce soit (par. remove(), empty(), html("")etc.).


Navigateurs plus anciens

Les navigateurs plus anciens - en particulier les anciennes versions d'IE - sont connus pour avoir des problèmes de fuite de mémoire en raison des écouteurs d'événements qui gardent des références aux éléments auxquels ils étaient attachés.

Si vous souhaitez une explication plus approfondie des causes, des modèles et des solutions utilisés pour corriger les fuites de mémoire de versions IE héritées, je vous recommande vivement de lire cet article MSDN sur la compréhension et la résolution des modèles de fuite d'Internet Explorer.

Quelques autres articles pertinents à ce sujet:

La suppression manuelle des écouteurs serait probablement une bonne habitude à prendre dans ce cas (uniquement si la mémoire est vitale pour votre application et que vous ciblez réellement de tels navigateurs).

dsgriffin
la source
22
Selon la documentation jquery lors de l'utilisation de la méthode remove () sur un élément, tous les écouteurs d'événements sont supprimés de la mémoire. Cela affecte l'élément lui-même et tous les nœuds enfants. Si vous souhaitez conserver les listeurs d'événements en mémoire, vous devez utiliser .detach () à la place. Utile lorsque les éléments supprimés vont être réinsérés sur le dom.
Lothre1
1
Si l'élément contient des éléments enfants, est-ce qu'il détachera également les listeurs d'événements sur les éléments enfants?
CBeTJlu4ok
1
@ Lothre1 - c'est uniquement lors de l'utilisation de la removeméthode. la plupart du temps, le DOM sera tout simplement effacé. (comme des liens turbo ou quelque chose). Je me demande comment la mémoire est affectée si je le fais document.body.innerHTML = ''...
vsync
1
J'aurais besoin de plus que d '"expérience personnelle", plus de données et de tests et de liens vers des spécifications qui disent comment la mémoire reste persistante sur les nœuds qui ne sont plus dans le document, c'est trop important pour se fier à la parole de quelqu'un sans preuve :)
vsync
1
@ Lothre1 Merci - J'ai creusé un peu plus et découvert à quel point jQuery agit différemment du JavaScript normal à cet égard. J'ai mis à jour la réponse.
dsgriffin
23

concernant jQuery:

la méthode .remove () retire les éléments du DOM. Utilisez .remove () lorsque vous souhaitez supprimer l'élément lui-même, ainsi que tout ce qu'il contient. En plus des éléments eux-mêmes, tous les événements liés et les données jQuery associés aux éléments sont supprimés. Pour supprimer les éléments sans supprimer les données et les événements, utilisez plutôt .detach ().

Référence: http://api.jquery.com/remove/

Code .remove()source de 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;
}

apparemment jQuery utilise node.removeChild()

Selon ceci: 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.

c'est-à-dire que les écouteurs d'événements peuvent être supprimés, mais nodeexistent toujours en mémoire.

Sreenath S
la source
3
Vous ne faites qu'ajouter de la confusion - jQuery ne fait rien avec des gestionnaires aussi simples que removeChildcela. Les deux vous renvoient également une référence que vous pouvez conserver pour rattacher cette dernière (auquel cas elle reste évidemment en mémoire) ou la jeter (auquel cas elle est finalement récupérée par GC et supprimée).
Oleg V. Volkov
je sais: D. alors où êtes-vous celui qui a édité la question? parce que j'aurais pu jurer qu'il y avait quelque chose à propos de l'utilisation de jquery pour supprimer un élément DOM, dans la question précédente. maintenant ma réponse sonne comme si j'expliquais des choses juste pour caresser mon ego. hé, vous pouvez toujours
downvote
8

N'hésitez pas à regarder le tas pour voir les fuites de mémoire dans les gestionnaires d'événements gardant une référence à l'élément avec une fermeture et l'élément gardant une référence au gestionnaire d'événements.

Le garbage collector n'aime pas les références circulaires.

Cas de fuite de mémoire habituel: admettre qu'un objet a une référence à un élément. Cet élément a une référence au gestionnaire. Et le gestionnaire a une référence à l'objet. L'objet a des références à beaucoup d'autres objets. Cet objet faisait partie d'une collection que vous pensez avoir jeté en le supprimant de votre collection. => l'objet entier et tout ce qu'il fait référence resteront en mémoire jusqu'à la sortie de la page. => vous devez penser à une méthode de destruction complète pour votre classe d'objets ou faire confiance à un framework mvc par exemple.

De plus, n'hésitez pas à utiliser la partie Arborescence de conservation des outils de développement Chrome.

lib3d
la source
8

Il suffit d'étendre d'autres réponses ...

Les gestionnaires d'événements délégués ne seront pas supprimés lors de la suppression de l'élément.

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

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

Maintenant, vérifiez:

$._data(document.body, 'events');
Lucky Soni
la source
9
Le gestionnaire d'événements est attaché au corps et non à #someEl, naturellement le gestionnaire ne doit pas être supprimé tant que le corps est toujours là.
Yangshun Tay
Cela peut être supprimé manuellement: stackoverflow.com/questions/22400907/…
fangxing
7

En ce qui concerne jQuery, les méthodes courantes suivantes supprimeront également d'autres constructions telles que les gestionnaires de données et d'événements:

retirer()

En plus des éléments eux-mêmes, tous les événements liés et les données jQuery associés aux éléments sont supprimés.

vide()

Pour éviter les fuites de mémoire, jQuery supprime les autres constructions telles que les gestionnaires de données et d'événements des éléments enfants avant de supprimer les éléments eux-mêmes.

html ()

De plus, jQuery supprime les autres constructions telles que les gestionnaires de données et d'événements des éléments enfants avant de remplacer ces éléments par le nouveau contenu.

Jaskey
la source
2

Oui, le garbage collector les supprimera également. Cela pourrait ne pas toujours être le cas avec les navigateurs hérités.

Darin Dimitrov
la source
7
votre déclaration doit être prise en charge par la documentation de l'API, des exemples, etc.
Paolo Fulgoni