eliminar - insertbefore javascript ejemplo
¿Los manejadores de eventos en un nodo DOM se eliminan con el nodo? (5)
(Nota: estoy usando jQuery a continuación, pero la pregunta es realmente una general de Javascript).
Digamos que tengo una div#formsection
cuyos contenidos se actualizan repetidamente usando AJAX, como este:
var formSection = $(''div#formsection'');
var newContents = $.get(/* URL for next section */);
formSection.html(newContents);
Cada vez que actualizo este div, disparo un evento personalizado , que vincula a los manejadores de eventos con algunos de los elementos recién agregados, como este:
// When the first section of the form is loaded, this runs...
formSection.find(''select#phonenumber'').change(function(){/* stuff */});
...
// ... when the second section of the form is loaded, this runs...
formSection.find(''input#foo'').focus(function(){/* stuff */});
Entonces: estoy vinculando controladores de eventos a algunos nodos DOM, y luego, eliminando esos nodos DOM e insertando nuevos ( html()
hace) y vinculando manejadores de eventos a los nuevos nodos DOM.
¿Se eliminan mis controladores de eventos junto con los nodos DOM a los que están vinculados? En otras palabras, al cargar nuevas secciones, ¿hay muchos manejadores de eventos inútiles acumulándose en la memoria del navegador, esperando eventos en los nodos DOM que ya no existen, o se eliminan cuando se eliminan los nodos DOM?
Pregunta de bonificación: ¿cómo puedo probar esto yo mismo?
No necesariamente
La documentación sobre el método empty()
jQuery responde mi pregunta y me da una solución a mi problema. Dice:
Para evitar fugas de memoria, jQuery elimina otras construcciones, como los manejadores de datos y eventos, de los elementos secundarios antes de eliminar los elementos.
Entonces: 1) si no hiciéramos esto explícitamente, obtendríamos fugas de memoria, y 2) al usar empty()
, puedo evitar esto.
Por lo tanto, debería hacer esto:
formSection.empty();
formSection.html(newContents);
Todavía no está claro si .html()
se encargaría de esto por sí mismo, pero una línea adicional para estar seguro no me molesta.
Es posible que deba eliminar esos controladores de eventos.
La memoria Javascript se filtra después de descargar una página web
En nuestro código, que no está basado en jQuery, sino en algunos prototipos desviados, tenemos inicializadores y destructores en nuestras clases. Descubrimos que es absolutamente esencial eliminar los controladores de eventos de los objetos DOM cuando destruimos no solo nuestra aplicación sino también widgets individuales durante el tiempo de ejecución.
De lo contrario, terminamos con pérdidas de memoria en IE.
Es sorprendentemente fácil obtener pérdidas de memoria en IE, incluso cuando descargamos la página, debemos asegurarnos de que la aplicación "se apaga" limpiamente, ordenando todo, o el proceso de IE crecerá con el tiempo.
Editar: para hacer esto correctamente, tenemos un observador de eventos en la window
para el evento de unload
. Cuando llega ese evento, nuestra cadena de destructores es llamada para limpiar adecuadamente cada objeto.
Y algunos ejemplos de código:
/**
* @constructs
*/
initialize: function () {
// call superclass
MyCompany.Control.prototype.initialize.apply(this, arguments);
this.register(MyCompany.Events.ID_CHANGED, this.onIdChanged);
this.register(MyCompany.Events.FLASHMAPSV_UPDATE, this.onFlashmapSvUpdate);
},
destroy: function () {
if (this.overMap) {
this.overMap.destroy();
}
this.unregister(MyCompany.Events.ID_CHANGED, this.onIdChanged);
this.unregister(MyCompany.Events.FLASHMAPSV_UPDATE, this.onFlashmapSvUpdate);
// call superclass
MyCompany.Control.prototype.destroy.apply(this, arguments);
},
Las funciones del manejador de eventos están sujetas a la misma recolección de basura que otras variables. Eso significa que se eliminarán de la memoria cuando el intérprete determine que no hay medios posibles para obtener una referencia a la función. Sin embargo, simplemente eliminar un nodo no garantiza la recolección de basura. Por ejemplo, tome este nodo y el controlador de eventos asociado
var node = document.getElementById(''test'');
node.onclick = function() { alert(''hai'') };
Ahora vamos a eliminar el nodo del DOM
node.parentNode.removeChild(node);
Por lo tanto, el node
ya no estará visible en su sitio web, pero claramente todavía existe en la memoria, al igual que el controlador de eventos.
node.onclick(); //alerts hai
Siempre que la referencia al node
siga siendo accesible de alguna manera, sus propiedades asociadas (de las cuales onclick
es una) permanecerán intactas.
Ahora intentémoslo sin crear una variable pendiente
document.getElementById(''test'').onclick = function() { alert(''hai''); }
document.getElementById(''test'').parentNode.removeChild(document.getElementById(''test''));
En este caso, parece que no hay otra forma de acceder al #test del nodo DOM, por lo que cuando se ejecuta un ciclo de recolección de basura, el manejador onclick
debe eliminarse de la memoria.
Pero este es un caso muy simple. El uso de cierres Javascript puede complicar en gran medida la determinación de la capacidad de recolección de basura. Intentemos enlazar una función del controlador de eventos un poco más compleja con onclick
document.getElementById(''test'').onclick = function() {
var i = 0;
setInterval(function() {
console.log(i++);
}, 1000);
this.parentNode.removeChild(this);
};
Por lo tanto, cuando haga clic en #test, el elemento se eliminará instantáneamente, sin embargo, un segundo después, y cada segundo después, verá un número incrementado impreso en su consola. El nodo se elimina y no es posible hacer más referencia a él, pero parece que aún quedan partes de él. En este caso, es probable que la función del controlador de eventos no se conserve en la memoria, pero sí el alcance que creó.
Entonces la respuesta que creo es; depende. Si hay referencias colgantes y accesibles a los nodos DOM borrados, sus manejadores de eventos asociados todavía residirán en la memoria, junto con el resto de sus propiedades. Incluso si este no es el caso, el alcance creado por las funciones del controlador de eventos aún podría estar en uso y en la memoria.
En la mayoría de los casos (y felizmente ignorando IE6) es mejor simplemente confiar en que Garbage Collector hará su trabajo, después de todo, Javascript no es C. Sin embargo, en casos como el último ejemplo, es importante escribir funciones de destructor de algún tipo para apagar la funcionalidad implícitamente.
Quería conocerme a mí mismo, así que después de una pequeña prueba, creo que la respuesta es sí.
Se llama a removeEvent cuando eliminas () algo del DOM.
Si quieres verlo tú mismo, puedes intentarlo y seguir el código estableciendo un punto de interrupción. (Estaba usando jquery 1.8.1)
Agregue un nuevo div primero:
$(''body'').append(''<div id="test"></div>'')
Marque$.cache
para asegurarse de que no hay eventos adjuntos. (debe ser el último objeto)
Adjunte un evento de clic a él:
$(''#test'').on(''click'',function(e) {console.log("clicked")});
$.cache
y vea un nuevo objeto en$.cache
:
$(''#test'').click()
$.cache
y puedes ver que el objeto en$.cache
se ha ido:
$(''#test'').remove()
jQuery hace todo lo posible para evitar pérdidas de memoria al eliminar elementos del DOM. Siempre que use jQuery para eliminar nodos DOM, jQuery debe manejar la eliminación de los controladores de eventos y los datos adicionales. Recomiendo leer los Secretos de un Ninja de JavaScript de John Resig, ya que analiza en detalle las posibles filtraciones en diferentes navegadores y cómo las bibliotecas de JavaScript como jQuery solucionan estos problemas. Si no está utilizando jQuery, definitivamente debe preocuparse por filtrar la memoria a través de manejadores de eventos huérfanos al eliminar los nodos DOM.