Se um elemento DOM for removido, seus ouvintes também serão removidos da memória?

Respostas:

307

Navegadores modernos

JavaScript simples

Se um elemento DOM removido é livre de referência (sem referências apontando para ele), então sim - o próprio elemento é capturado pelo coletor de lixo, bem como pelos manipuladores / ouvintes de eventos associados a ele.

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.

Contudo; se houver referências que ainda apontem para o referido elemento, o elemento e seus ouvintes de eventos serão retidos na memória.

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

Seria justo supor que os métodos relevantes no jQuery (como remove()) funcionariam exatamente da mesma maneira (considerando que remove()foi escrito usando, removeChild()por exemplo).

No entanto, isso não é verdade ; a biblioteca jQuery realmente possui um método interno (que não é documentado e, em teoria, pode ser alterado a qualquer momento) chamado cleanData() (eis como esse método se parece ) que limpa automaticamente todos os dados / eventos associados a um elemento após a remoção do DOM (seja esta via. remove(), empty(), html("")etc.).


Navegadores mais antigos

Navegadores mais antigos - especificamente versões mais antigas do IE - são conhecidos por terem problemas de vazamento de memória devido ao fato de os ouvintes de eventos manterem as referências aos elementos aos quais foram anexados.

Se você quiser uma explicação mais aprofundada das causas, padrões e soluções usadas para corrigir vazamentos de memória da versão IE herdada, recomendo totalmente que você leia este artigo do MSDN sobre Compreendendo e resolvendo padrões de vazamento do Internet Explorer.

Mais alguns artigos relevantes para isso:

Remover você mesmo os ouvintes manualmente provavelmente seria um bom hábito para entrar nesse caso (apenas se a memória for vital para o seu aplicativo e você estiver realmente direcionando esses navegadores).

dsgriffin
fonte
22
De acordo com a documentação do jquery ao usar o método remove () sobre um elemento, todos os ouvintes de eventos são removidos da memória. Isso afeta o elemento selft e todos os nós filhos. Se você deseja manter os ouvintes de eventos na memória, use .detach (). Útil quando os elementos removidos serão inseridos novamente no dom.
Lothre1
11
Se o elemento contiver elemens filhos, ele também desanexará os ouvintes de eventos nos elementos filhos?
precisa saber é o seguinte
11
@ Lothre1 - isso é apenas quando se usa o removemétodo. na maioria das vezes, o DOM será apagado completamente. (como links turbo ou algo assim). Eu estou querendo saber como a memória é afetada se eu fizer document.body.innerHTML = ''...
vsync
11
Eu precisaria de mais do que "experiência pessoal", mais como dados concretos, testes e links para especificações que dizem como a memória se mantém persistente em nós que não estão mais no documento, isso é importante demais para confiar apenas na palavra de alguém sem provas :)
vsync
11
@ Lothre1 Obrigado - Eu analisei um pouco mais e descobri como o jQuery age de maneira diferente do JavaScript comum a esse respeito. Atualizou a resposta.
dsgriffin
23

em relação ao jQuery:

o método .remove () remove elementos do DOM. Use .remove () quando quiser remover o próprio elemento, bem como tudo o que estiver dentro dele. Além dos próprios elementos, todos os eventos vinculados e dados do jQuery associados aos elementos são removidos. Para remover os elementos sem remover dados e eventos, use .detach ().

Referência: http://api.jquery.com/remove/

Código .remove()fonte do 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;
}

aparentemente o jQuery usa node.removeChild()

De acordo com isso: 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.

ou seja, os ouvintes de eventos podem ser removidos, mas nodeainda existem na memória.

Sreenath S
fonte
3
Você está apenas adicionando confusão - o jQuery não faz nada disso com manipuladores tão simples removeChild. Os dois também retornam uma referência que você pode manter para recolocar o último (nesse caso, obviamente, permanece na memória) ou jogar de maneira (nesse caso, é eventualmente captado pelo GC e removido).
Oleg V. Volkov
Eu sei. Então, onde você foi quem editou a pergunta? Porque eu poderia jurar que havia algo sobre o uso do jquery para remover um elemento DOM, na pergunta anterior. Agora minha resposta parece que estou explicando as coisas apenas para acariciar meu ego. Ei, você sempre pode
reduzir o voto
8

Não hesite em observar o heap para ver vazamentos de memória nos manipuladores de eventos, mantendo uma referência ao elemento com um fechamento e o elemento mantendo uma referência ao manipulador de eventos.

Coletor de lixo não gosta de referências circulares.

Caso usual de vazamento de memória: admita que um objeto tenha uma ref para um elemento. Esse elemento tem uma referência ao manipulador. E o manipulador tem uma referência ao objeto. O objeto tem referências a muitos outros objetos. Esse objeto fazia parte de uma coleção que você acha que jogou fora, sem referência a ele da sua coleção. => todo o objeto e tudo o que ele se refere permanecerão na memória até a saída da página. => você precisa pensar em um método completo de eliminação para sua classe de objeto ou confiar em uma estrutura mvc, por exemplo.

Além disso, não hesite em usar a parte da árvore de retenção das ferramentas de desenvolvimento do Chrome.

lib3d
fonte
8

Apenas estendendo outras respostas ...

Os manipuladores de eventos delegados não serão removidos após a remoção do elemento.

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

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

Agora verifica:

$._data(document.body, 'events');
Lucky Soni
fonte
9
O manipulador de eventos está anexado ao corpo e não #someEl; naturalmente, o manipulador não deve ser removido enquanto o corpo ainda estiver aqui.
Yangshun Tay 27/07
Isso pode ser removido manualmente: stackoverflow.com/questions/22400907/…
fangxing
7

Em relação a jQuery, os seguintes métodos comuns também removerão outras construções, como manipuladores de dados e eventos:

retirar()

Além dos próprios elementos, todos os eventos vinculados e dados do jQuery associados aos elementos são removidos.

esvaziar()

Para evitar vazamentos de memória, o jQuery remove outras construções, como manipuladores de dados e eventos dos elementos filhos, antes de remover os próprios elementos.

html ()

Além disso, o jQuery remove outras construções, como manipuladores de dados e eventos dos elementos filhos, antes de substituir esses elementos pelo novo conteúdo.

Jaskey
fonte
2

Sim, o coletor de lixo também os removerá. Nem sempre pode ser o caso de navegadores herdados.

Darin Dimitrov
fonte
7
você declaração deve ser suportado por documentação da API, exemplos, etc.
Paolo Fulgoni