web-dev-qa-db-fra.com

Dois-je supprimer des écouteurs d'événements avant de supprimer des éléments?

Si j'ai un élément parent avec des enfants qui ont des écouteurs d'événements liés, dois-je supprimer ces écouteurs d'événements avant de supprimer le parent? (c'est à dire., parent.innerHTML = '';) Pourrait-il y avoir des fuites de mémoire si les écouteurs d'événements ne sont pas dissociés d'un élément s'il est supprimé du DOM?

71
user369450

Réponse courte: oui

Réponse longue: la plupart des navigateurs gèrent cela correctement et suppriment ces gestionnaires eux-mêmes. Il existe des navigateurs plus anciens (IE 6 et 7, si je me souviens bien) qui gâchent cela. Oui, il pourrait y avoir des fuites de mémoire. Vous ne devriez pas avoir à vous en préoccuper, mais vous devez le faire. Jetez un oeil à ce document .

36
jwueller

Juste pour mettre à jour les informations ici. J'ai testé divers navigateurs, en particulier pour les fuites de mémoire pour les écouteurs d'événements dépendants de manière circulaire sur les événements de chargement iframe.

Le code utilisé (jsfiddle interfère avec les tests de mémoire, alors utilisez votre propre serveur pour le tester):

<div>
    <label>
        <input id="eventListenerCheckbox" type="checkbox" /> Clear event listener when removing iframe
    </label>
    <div>
        <button id="startTestButton">Start Test</button>
    </div>
</div>

<div>
    <pre id="console"></pre>
</div>

<script>

    (function() {
        var consoleElement = document.getElementById('console');
        window.log = function(text) {
            consoleElement.innerHTML = consoleElement.innerHTML + '<br>' + text;
        };
    }());

    (function() {
        function attachEvent(element, eventName, callback) {
            if (element.attachEvent)
            {
                element.attachEvent(eventName, callback);
            }
            else
            {
                element[eventName] = callback;
            }
        }

        function detachEvent(element, eventName, callback) {
            if (element.detachEvent)
            {
                element.detachEvent(eventName, callback);
            }
            else
            {
                element[eventName] = null;
            }
        }

        var eventListenerCheckbox = document.getElementById('eventListenerCheckbox');
        var startTestButton = document.getElementById('startTestButton');
        var iframe;
        var generatedOnLoadEvent;

        function createOnLoadFunction(iframe) {
            var obj = {
                increment: 0,
                hugeMemory: new Array(100000).join('0') + (new Date().getTime()),
                circularReference: iframe
            };

            return function() {
                // window.log('iframe onload called');
                obj.increment += 1;
                destroy();
            };
        }

        function create() {
            // window.log('create called');
            iframe = document.createElement('iframe');

            generatedOnLoadEvent = createOnLoadFunction(iframe);
            attachEvent(iframe, 'onload', generatedOnLoadEvent);

            document.body.appendChild(iframe);
        }

        function destroy() {
            // window.log('destroy called');
            if (eventListenerCheckbox.checked)
            {
                detachEvent(iframe, 'onload', generatedOnLoadEvent)
            }

            document.body.removeChild(iframe);
            iframe = null;
            generatedOnLoadEvent = null;
        }

        function startTest() {
            var interval = setInterval(function() {
                create();
            }, 100);

            setTimeout(function() {
                clearInterval(interval);
                window.log('test complete');
            }, 10000);
        }

        attachEvent(startTestButton, 'onclick', startTest);
    }());

</script>

S'il n'y a pas de fuite de mémoire, la mémoire utilisée augmentera d'environ 1000 Ko ou moins après l'exécution des tests. Cependant, s'il y a une fuite de mémoire, la mémoire augmentera d'environ 16 000 Ko. La suppression de l'écouteur d'événements entraîne d'abord une utilisation moindre de la mémoire (aucune fuite).

Résultats:

  • IE6 - fuite de mémoire
  • IE7 - fuite de mémoire
  • IE8 - aucune fuite de mémoire
  • IE9 - fuite de mémoire (???)
  • IE10 - fuite de mémoire (???)
  • IE11 - aucune fuite de mémoire
  • Edge (20) - aucune fuite de mémoire
  • Chrome (50) - aucune fuite de mémoire
  • Firefox (46) - difficile à dire, ne fuit pas mal, alors peut-être juste un garbage collector inefficace? Finit avec 4 Mo supplémentaires sans raison apparente.
  • Opera (36) - pas de fuite de mémoire
  • Safari (9) - aucune fuite de mémoire

Conclusion: les applications Bleeding Edge peuvent probablement éviter de supprimer les écouteurs d'événements. Mais je considérerais quand même que c'est une bonne pratique, malgré l'ennui.

44
Dwight