¿Necesito eliminar los detectores de eventos antes de eliminar elementos?

84

Si tengo un elemento padre con hijos que tienen oyentes de eventos vinculados a ellos, ¿debo eliminar esos oyentes de eventos antes de borrar el padre? (es decir, parent.innerHTML = '';) ¿Podría haber pérdidas de memoria si los detectores de eventos no se desvinculan de un elemento si se elimina del DOM?

Todos los trabajadores son esenciales
fuente

Respuestas:

51

Respuesta corta: si

Respuesta larga: la mayoría de los navegadores manejan esto correctamente y eliminan esos controladores. Hay algunos navegadores más antiguos (IE 6 y 7, si recuerdo bien) que están estropeando esto. Sí, podría haber pérdidas de memoria. No debería tener que preocuparse por esto, pero es necesario. Eche un vistazo a este documento .

jwueller
fuente
De hecho: aunque la mayoría de los navegadores actuales no lo sufrirán tanto, IE 7 todavía se usa comúnmente. También eche un vistazo a los patrones de fuga de memoria en JavaScript .
Marcel Korpel
7
¿Alguien tiene el conocimiento suficiente para actualizar esto para el mercado actual de navegadores? ¿O vale la pena hacer una pregunta aparte? IE7 pensé que estaba prácticamente eliminado , mientras que ie8 todavía está rondando. ¿IE8 maneja oyentes de eventos abandonados?
Aidan Miles
28
6 años después, creo que IE < 10se puede considerar obsoleto y no utilizado por nadie que vaya a sitios que no sean Yahoo y AOL en este momento. Cualquiera que use IE de forma poco irónica en este punto es más probable que sea víctima de una estafa telefónica india o contraiga un virus que tener problemas con los controladores de eventos que ralentizan su navegador.
Braden Best
67

Solo para actualizar la información aquí. He estado probando varios navegadores, específicamente para detectar fugas de memoria para detectores de eventos dependientes circularmente en eventos de carga de iframe.

El código utilizado (jsfiddle interfiere con las pruebas de memoria, así que use su propio servidor para probar esto):

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

Si no hay pérdida de memoria, la memoria utilizada aumentará alrededor de 1000 kb o menos después de que se ejecuten las pruebas. Sin embargo, si hay una pérdida de memoria, la memoria aumentará en aproximadamente 16.000 kb. Quitar primero el detector de eventos siempre da como resultado un menor uso de memoria (sin fugas).

Resultados:

  • IE6 - pérdida de memoria
  • IE7 - pérdida de memoria
  • IE8: sin pérdida de memoria
  • IE9 - pérdida de memoria (???)
  • IE10 - pérdida de memoria (???)
  • IE11 - sin pérdida de memoria
  • Edge (20): sin pérdida de memoria
  • Chrome (50): sin pérdida de memoria
  • Firefox (46): es difícil de decir, no se filtra mucho, así que ¿tal vez solo un recolector de basura ineficiente? Termina con 4 MB adicionales sin motivo aparente.
  • Opera (36) - sin pérdida de memoria
  • Safari (9): sin pérdida de memoria

Conclusión: las aplicaciones de Bleeding Edge probablemente puedan salirse con la suya sin eliminar los detectores de eventos. Pero aún lo considero una buena práctica, a pesar de la molestia.

Dwight
fuente