イベントリスナの削除

このQ&Aのポイント
  • イベントリスナの削除方法について教えてください。
  • あるオブジェクトに登録されたイベントを破棄する方法を教えてください。
  • 非同期通信によってオブジェクトを差し替える際に、登録されたイベントを破棄する方法を知りたいです。
回答を見る
  • ベストアンサー

イベントリスナの削除

あるオブジェクトとそれ以外のいくつかのオブジェクトを含むドキュメントがあります。 そのドキュメント全体のうち、例えば、すべてのa要素に対して、clickイベントを追加したとします。 また、そのドキュメントのうち「あるオブジェクト」だけを非同期通信によって差し替えます。 この時、差し替えられる前の「あるオブジェクト」に登録されたイベントが残ったまま、新しいオブジェクトに差し替えられてしまいます。 これを何度も繰り返していくことで、UAは悲鳴を上げているようなのです。(具体的には、ページ内リンクを辛そうにしています) 解消するには、『「あるオブジェクト」だけを非同期通信によって差し替え』る直前に、「あるオブジェクト」に登録されたイベントをすべて破棄すればいいと思うのです。 ですが、登録されたイベントの一部は引数を与える必要があったため、 Obj.addEventListener(eType, (function(arg) {  return function(event)  {   //こう書くと、argにaが、eventにイベントオブジェクトが渡される  } })(a), false); このような記述をしており、関数名がありません。 そこで、あるオブジェクトに対して、登録されたイベントをすべて破棄する、良い方法があればご教授頂きたく、質問させていただきます。 よろしくお願いいたします。

noname#144089
noname#144089

質問者が選んだベストアンサー

  • ベストアンサー
  • Chaire
  • ベストアンサー率60% (79/130)
回答No.2

No.1 訂正: x クリックの発生源は、必ず「最深のノード」 o クリックの発生源は、必ず「最深の要素ノード」 ノードとデータを紐付ける方法としては、簡単なのは ID を使うことです。サーバ側で動的に要素ノードを生成するなら UUID でも使えば手っ取り早いでしょう。 その他、ノードの位置関係・属性・内容など、あらゆるものがノードの目印になりえます。それこそ、CSS セレクタによるパターンマッチングの出番です(Selectors API Level 2 なら element.matchesSelector())。 目印になりそうなものがなければ、直接 HTML のイベント属性(onclick="...")として書いた方がマシです。そうすれば、他のスクリプトが innerHTML をごっそり書き換えるような乱暴な行為をしても、イベント属性が生き残る可能性が高くなります(一般に、ノードを複製してもイベントリスナは複製されません)。 どうしても、いわゆるクロージャでデータとノードを保持しなければならないのであれば、 (function (node) {  function _click(e) { ... }  function _remove(e) {   node.removeEventListener('click', _click, false);   node.removeEventListener('DOMNodeRemovedFromDocument', _remove, false);   node = _click = _remove = null;  }  node.addEventListener('click', _click, false);  node.addEventListener('DOMNodeRemovedFromDocument', _remove, false); })(node); とか何とかでっち上げることはできるでしょう(DOMNodeRemoved(FromDocument) のサポート状況を確認してはいませんが)。関数名が必要なら、付ければ良いだけです。名前ありと名前なしの関数に本質的な違いはありません。 あるいは、除去されるノードがあらかじめ分かっているのであれば、そのノードとイベントリスナをグローバル変数にでも保持しておき、文書から取り除かれる際に removeEventListener() すれば良いだけです。 いずれにせよ、俗に言うクロージャの多用は避けた方が無難です。切れる依存関係は切って下さい。

noname#144089
質問者

お礼

詳細な回答ありがとうございます。 ソースを見てみると、怖いくらいのクロージャが…。 さっそく書きなおしてきます。 本当にありがとうございました。

その他の回答 (1)

  • Chaire
  • ベストアンサー率60% (79/130)
回答No.1

そもそも論として、イベントリスナの取り付け方を「間違って」います。 JavaScript エンジンであればガベージコレクションが働くはずですが、 > (function(arg) { return function(event) { ... }})(a) このようにすると a への参照が残りますので、メモリリークの原因となります。有名どころでは IE6/7、Firefox 1.5、Safari 3.1 などがそうで、これらについては修正されているものの、実は結構厄介な問題です。 では、どうすれば良いのか。DOM Events(および IE Event Model)にはバブリングという概念があります。 <body><p><a>...</a></p></body> 上のような HTML 断片があるとき、a をクリックすると、それは p へのクリックでもあり、さらに body へのクリックでもあります。裏を返せば、body でクリックを監視すれば、a および p で生じたクリックも感知できます。 突き詰めば、クリックは文書木の頂点である document で監視すれば事足りるわけです。 document.addEventListener('click', function(event) {  event.target; // クリックの発生源(a、p)  event.currentTarget; // イベントリスナが取り付けられているノード(慣習的に this と同じ) }, false); クリックの発生源は、必ず「最深のノード」です。そこから祖先ノードに向かってイベントが「バブル」していきます。バブリングの終点は document(HTML5 なら window)です。クリックの発生源が、目標とするノードであるかを調べ、適切な処理に渡せるよう「ハンドル」します。 こうすれば、イベントリスナ自体は一個で済みますし、新しく追加したノードがあろうと、削除されたノードがあろうと、全く影響を受けません。 個々のノードにイベントハンドラを取り付ける、という NN4 以来の方法に頭が慣れていると戸惑うかもしれませんが、要するにページ全体をキャンバスと見なし、キャンバスのどこがクリックされたか、というごくごく普通の考え方です。 これにもいくつかの細かい問題がありますが、詳細は上述のキーワード等でお調べ下さい。少なくとも、個々のノードにイベントハンドラを取り付けるようなやり方は、他に方法がないときの最後の手段だと思って構いません。

noname#144089
質問者

お礼

回答ありがとうございます。 長くスクリプトを書いてはいましたが、プログラミングは始めたばかりなので、戸惑いとかはありません。 ただ、いいことを聞いたという感じです。 今までは、結構頻繁にイベントを消したり足したりしていたので、、。

関連するQ&A

  • イベントリスナーの部分を関数にしたい

    イベントリスナーの部分で、「load」以外に「change」も必要になったので関数にしたいのですが、 引数の関数の指定方法がわかりません。 どうすればいいのでしょうか。よろしくお願いします。 【イベントリスナーを関数にする前】 function hoge(){ this.view = function(){ var _this = this; window.addEventListener( 'load',function(){ _this.foo()}, false ); } this.foo =function(){ var txt = document.createTextNode( this.moji ); document.body.appendChild( txt ); } } var a =new hoge(); a.moji="テスト"; a.view(); 【やってみたこと】 function hoge(){ this.view = function(){ var _this = this; var func = function(){ _this.foo()}; this.addListener( 'window', 'load', func ); } this.addListener = function(elem,type,func){ elem.addEventListener( type,func, false ); } this.foo =function(){ var txt = document.createTextNode( this.moji ); document.body.appendChild( txt ); } } var a =new hoge(); a.moji="テスト"; a.view();

  • イベントリスナーで読み込んだ後に、DOMで文字を表

    イベントリスナーで読み込んだ後に、DOMで文字を表示させたい。 DOMを使って、文字を表示させるならイベントリスナーで読み込み必要があると思いました。 そこで、下記のように書いて試してみました。 function hoge(){ this.view = function(){ window.addEventListener( 'load',this.foo, false ); } this.foo =function(){ var txt = document.createTextNode( this.moji ); document.body.appendChild( txt ); } } var a =new hoge(); a.moji="テスト"; a.view(); すると、「undefined」と表示されます。 どうすればいいのでしょうか。よろしくお願いします。

  • 複数のイベントリスナーを設置するのは正しい?

    クリック時、マウスオーバー時、マウスアウト時の処理を作るとき、 イベントリスナーの使い方はこのようなやり方でよいでしょうか。 var d = document.getElement.ById("id"); d.addEventListener('click', function(e){ var tar=e.target; tar.style.backgroundColor ="#000000"; },false); d.addEventListener('mouseover', function(e){ var tar=e.target; tar.style.backgroundColor ="#aaaaaa"; },false); d.addEventListener('mouseout', function(e){ var tar=e.target; tar.style.backgroundColor ="#555555"; },false);

  • JSのイベントターゲット が難しい

    JSのイベントターゲット (EventTarget)が難しくてよくわからないのですが初心者にもわかるように解説していただけるとありがたいです。 https://developer.mozilla.org/en-US/docs/Web/API/EventTarget イベントターゲット(EventTarget)は、DOMイベントを受け取り、それらへのリスナーを持つことが出来るオブジェクトによって実装されるDOMインターフェースです。 Elementと、 documentと、 windowは、ほとんどの共通イベントターゲットですが、 例えば、XMLHttpRequest、AudioNode、AudioContextなど、 他のオブジェクトもエベントターゲットになることが可能です。 多くのイベントターゲット(Element、document、windowを含む)は、 onXXXプロパティと属性を介して、 イベントハンドラの設定もサポートします。 メソッド .addEventListener() 要素にイベントハンドラを登録します。 .dispatchEvent() DOM内のノードのイベントを実行します。 .removeEventListener() EventTarget.addEventListenerを使用して登録されたイベントリスナーを削除します。 イベントを発動させる要素につけるイベントファンクションのようなものなのでしょうか?

  • イベントリスナーがうまくいかない

    delBtnがない場合にイベントリスナーを読み込むとエラーが出てその後の処理が行われないため 下記のようにあった場合のみ予約するようにしたところ今度はクリックしても実行されない問題が起きました。 読み込み時は、エラーにならないようにし、かつボタンを押したときに要素を削除するようにするにはどうしたらよいでしょうか? var Result = document.getElementsByClassName('Result')[0].innerHTML = '<span class="u-mr-1rem">あああああ</span><input id="DelBtn" type="button" value="削除">; var Result = document.createElement('p'); BtnWrap.appendChild(Result); Result.classList.add('Result u-mtb-1rem'); var delBtn = document.getElementById('DelBtn'); if(delBtn !== null) { delBtn.addEventListener('click', function() { document.getElementsByClassName('Result')[0].remove(); }); }

  • インラインフレーム内の変数を親ページから呼び出す

    インラインフレーム内のマウス位置を↓のjavascriptコードで取得しているのですが、親ページのjavascriptの関数で使用したいです。どのようにインラインフレーム内の変数を親ページから呼び出して、利用すればよいのでしょうか? var x, y; window.addEventListener("DOMContentLoaded", function(){ window.document.onmousemove = function(e){ x = getMousePosition(e).x; } },false); function getMousePosition(e) { var obj = new Object(); if(e) { obj.x = e.pageX; obj.y = e.pageY; } else { obj.x = event.x + document.body.scrollLeft; obj.y = event.y + document.body.scrollTop; } return obj; }

  • イベントリスナに登録される function(e){} の "e" はeventオブジェクト?

    私はイベントリスナ登録時の匿名関数に渡す引数eの意味が今まで理解できていませんでした。 最近、下記コードを実行することで、 <script type='text/javascript'> window.addEventListener ('click', function(e){ console.info(e); // 引数をコンソール表示 (要Firebug) for (p in e){ console.info(p + ' = ' + e[p]); // プロパティを列挙 } }, false); </script> 「eventオブジェクトを渡しているらしい」と朧気ながら理解できました。 ただ、疑問点も残ります。 私の理解では、匿名関数は (function(str){ alert(str); })('Hello'); のように明示的に引数を渡さなければ、引き渡された値は undefined となるはずでした。 変数eの値はどこから出現したのでしょうか? そもそも、変数eはeventオブジェクトなのでしょうか?

  • イベントハンドラの関数の引数について

    こんにちは。 Object.onmouseoverのイベントハンドラにObject.classNameとeventを引数とする関数(func1)を指定するにはどのような風にしたら良いでしょうか?以下のように 試しましたがいずれも動きません。 function func1(obj,event){・・・} (1)Object.onmouseover=function(){func1(Object.className,event)} //エラー:Object.classNameはNull (2)Object.onmouseover=function(Object.className,event){func1(Object.className,event)} //無反応 (3)Object.onmouseover="func1("+Object.className+",event)"; //onmouseoverでない時にfunc1が呼び出される。 よろしくお願いいたします。

  • javascript 親オブジェクトと子オブジェクトを分離させたい

    例えばjavascriptで下のようなコードを書いたとします。 a=new Array() a[0]=new OBJ(); function OBJ(){ this.prop=document.getElementById('aaa') this.prop2=document.getElementById('bbb') } a[0].prop.addEventListener(click,func,false); function func(){ ??? } func()関数内でthisを使うとa[0].propを参照できます。 一方、a[0].prop2を参照するにはどうすればいいのでしょうか? 配列の要素番号を指定しないで参照させる方法が分からず悩んでいます。 下のようにもしできるのであればいいのですが。。。 a[0]とpropのオブジェクトを分割->a[0]オブジェクトを抜き出し-> a[0]オブジェクトにprop2を追加 または別の方法があるのでしょうか。 ぜひぜひご回答のほどお願いいたします。

  • 引数付きでイベントをセットしたい

    下のようにして引数を付けてイベントをセットしようとしたのですが、 どうも動作がうまくいってないようなのです。引数なし>Obj.onclick = check; ならちゃんと動作しました。どうすれば引数付きでセットできるのでしょうか? function check(text) { alert(text); } function sample() Obj = document.getElementById("sample"); Obj.onclick = check("メッセージ");

専門家に質問してみよう