DOM要素が削除された場合、そのリスナーもメモリから削除されますか?
普通のJavaScript
削除されたDOM要素が参照を含まない(参照を指していない)場合、yes - 要素自体は、ガベージコレクタによって次のように取得されます。それに関連付けられているすべてのイベントハンドラ/リスナ.
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.
しかしながら;それでもその要素を指す参照がある場合、その要素とそのイベントリスナーはメモリに保持されます。
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
JQueryの関連メソッド(remove()
など)もまったく同じように機能すると想定してもかまいません(remove()
がremoveChild()
を使用して記述されていることを考えると)。
しかし、これは正しくありません。 jQueryライブラリは実際にはcleanData()
これはこのメソッドのようなものです と呼ばれる内部メソッド(文書化されておらず、理論上いつでも変更することができます)を持っています。 DOMから削除されたときの要素(これはvia。remove()
、empty()
、html("")
などで)。
古いブラウザ、特にIEの古いバージョンでは、イベントリスナがそれらがアタッチされていた要素への参照を保持し続けているためにメモリリークの問題があることが知られています。
レガシーIEバージョンのメモリリークを修正するために使用された原因、パターン、および解決策について、より詳細な説明が必要な場合は、 こちらをお読みください。 MSDNのInternet Explorerのリークパターンの理解と解決に関する記事
これに関連する記事がさらにいくつかあります。
この場合、リスナーを自分で手動で削除するのがおそらく賢い習慣です(メモリがアプリケーションにとって非常に重要であり、実際にそのようなブラウザをターゲットにしている場合のみ)。
jQueryに関して:
.remove()メソッドはDOMから要素を取り出します。要素自体とその中のすべての要素を削除したい場合は、.remove()を使用してください。要素自体に加えて、要素に関連付けられているすべてのバインド済みイベントとjQueryデータが削除されます。データやイベントを削除せずに要素を削除するには、代わりに.detach()を使用してください。
参照先: http://api.jquery.com/remove/
jQuery v1.8.2 .remove()
ソースコード:
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;
}
どうやらjQueryはnode.removeChild()
を使っています
これによると: 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.
つまり、イベントリスナは削除されるかもしれませんが、node
はまだメモリに存在します。
イベントハンドラーがクロージャーを使用して要素への参照を保持し、要素がイベントハンドラーへの参照を保持している場合に、メモリリークが発生しないようにヒープを監視することを躊躇しないでください。
ガベージコレクタは循環参照を好まない。
通常のメモリリークの場合:オブジェクトに要素への参照があることを認めます。その要素にはハンドラへの参照があります。そしてハンドラはオブジェクトへの参照を持ちます。オブジェクトは他の多くのオブジェクトを参照しています。このオブジェクトは、コレクションからそれを参照しないことで捨てたと思うコレクションの一部です。 =>オブジェクト全体とそれが参照するすべてのものは、ページが終了するまでメモリに残ります。 =>あなたはあなたのオブジェクトクラスのための完全なkillメソッドについて考えるか、例えばmvcフレームワークを信頼しなければなりません。
さらに、Chrome開発ツールの保持ツリー部分を使用することを躊躇しないでください。
他の答えを拡張するだけで...
デリゲートイベントハンドラは要素の削除時に削除されません。
$('body').on('click', '#someEl', function (event){
console.log(event);
});
$('#someEL').remove(); // removing the element from DOM
今チェック:
$._data(document.body, 'events');
はい、ガベージコレクタもそれらを削除します。ただし、従来のブラウザでは必ずしもそうとは限りません。