2012-05-04 7 views
29

IのEventListenerを追加するには、次のコードを持っているのJavascriptにremoveEventListenerに動作していない

area.addEventListener('click',function(event) { 
       app.addSpot(event.clientX,event.clientY); 
       app.addFlag = 1; 
      },true); 
それは、次のコード

area.removeEventListener('click',function(event) { 
       app.addSpot(event.clientX,event.clientY); 
       app.addFlag = 1; 
      },true); 
を使用して、私はイベントリスナーを削除しようとした別の関数にexpected..Laterとして正常に動作している

偶数リスナーは削除されません。どうしてですか?removeEventListener()に問題はありますか? 注:ここで面積は、それを削除変数で関数を保存したり、単に名前の関数を使用してremoveEventListenerコールにその関数を渡すためのdocument.getElementById(「MYID」)

答えて

45

これは、2つの匿名関数が完全に異なる関数であるためです。 removeEventListenerの引数は、以前に添付された関数オブジェクトへの参照ではありません。

function foo(event) { 
       app.addSpot(event.clientX,event.clientY); 
       app.addFlag = 1; 
      } 
area.addEventListener('click',foo,true); 
area.removeEventListener('click',foo,true); 
+0

ありがとうございました! – Woppi

+9

+1真実。 'bind(this)'は署名を変更します。したがって、関数 'bind' APIを使用して' this'をバインドした後、常に 'var'に関数を代入し、同じ' var'を 'removeListener'で使用できるようにしてください。あなたはこの問題をよりよく現われています。 – Nirus

+2

これは、関数のパラメータを渡すことを許可しません。 'foo(1)' – Herrgott

0

のようなものです:

function areaClicked(event) { 
    app.addSpot(event.clientX, event.clientY); 
    app.addFlag = 1; 
} 

area.addEventListener('click', areaClicked, true); 
// ... 
area.removeEventListener('click', areaClicked, true); 
+0

しかし、私はその関数に引数(ここではイベント)を渡すことができます。そのため、私は匿名関数を使用しています。 –

+0

ブラウザに渡されます。関数を個別に定義するかどうかは関係ありません。 – ThiefMaster

+0

警告:私のアプローチで何が間違っているかを知りました。 removeEventListener()メソッドは、NAMED FUNCTIONSでのみ機能します。それは匿名関数では機能しません! これを考慮に入れてコードを編集したところ、すべてが計画通りに機能しました。クロージャーでNAMED関数を定義し、クロージャーによって渡されたパラメーターを使用してそのインスタンスへの参照を戻す必要があります。これを行い、removeEventListener()は完全に機能します。 –

0

あなたは両方の呼び出しで2つの異なる機能を作成している、最初の

、その後

area.addEventListener('click',handler); 
area.removeEventListener('click',handler); 
2

をあなたのイベントハンドラを定義します。したがって、2番目の関数は最初の関数には何ら関連しておらず、エンジンは関数を削除できます。代わりに関数の共通識別子を使用してください。

var handler = function(event) { 
       app.addSpot(event.clientX,event.clientY); 
       app.addFlag = 1; 
      }; 
area.addEventListener('click', handler,true); 

後のあなたは、あなたがイベントリスナーによって呼び出される関数にローカル変数を渡したい場合は、あなたが取得する(関数内の関数を定義することができます

area.removeEventListener('click', handler,true); 
0

を呼び出すことにより、ハンドラを削除することができますローカル変数)を呼び出し、関数自体の中で関数の名前を渡します。例えば、appをローカル変数としてイベントリスナーを追加する関数の中から始めましょう。この関数の中に、

function yourFunction() { 
    var app; 

    function waitListen() { 
     waitExecute(app, waitListen); 
    } 

    area.addEventListener('click', waitListen, true); 
} 

のような関数を書くと、waitExecuteが呼び出されたときに削除する必要があります。

function waitExecute (app, waitListen) { 
    ... // other code 
    area.removeEventListener('click', waitListen, true); 
} 
+0

ここで問題が発生しました。イベントハンドラ関数を定義し、その関数への参照を保存してから、あとでその参照をremoveEventListener()に渡しても、その関数は削除されません。 コメントを投稿するには小さすぎるので、コードが必要な場合は、回答ボックスを使用する必要があります。 –

+0

上記の補足:私が見つけた別の興味深い現象は、あなたのイベントリスナーはパッシブですが、古いものはチェーン内に残っています。さらに悪いことに、古いものはブロッキングイベントハンドラになり、新しいものはパッシブステータスを維持します。 ここで説明が必要だと思います。 –

0

説明が必要なremoveEventListener()に問題が発生しました。

私は、イベントリスナーにパラメータを渡すことができたかったので、イベントリスナーを生成する関数を作成しました。それはコールバックとして意図したイベントリスナーを呼び出す2番目の関数を返します。次のように

完全なライブラリファイルがある:

//Event handler constants 

function EventHandlerConstants() 
{ 
this.SUCCESS = 0; //Signals success of an event handler function 
this.NOTFUNCTION = 1; //actualHandler argument passed to MakeEventHandler() is not a Function object 

//End constructor 
} 

//MakeEventHandler() 

//Arguments: 

//actualHandler : reference to the actual function to be called as the true event handler 

//selfObject : reference to whatever object is intended to be referenced via the "this" keyword within 
//   the true event handler. Set to NULL if no such object is needed by your true 
//   event handler specified in the actualHandler argument above. 

//args  : array containing the arguments to be passed to the true event handler, so that the true 
//   event handler can be written with named arguments, such as: 

//   myEventHandler(event, arg1, arg2, ...) 

//   If your function doesn't need any arguments, pass an empty array, namely [], as the 
//   value of this argument. 

//Usage: 

//c = new EventHandlerConstants(); 
//res = MakeEventHandler(actualHandler, selfObject, args); 
//if (res == c.SUCCESS) 
// element.addEventListener(eventType, res.actualHandler, true); //or whatever 


function MakeEventHandler(actualHandler, selfObject, args) 
{ 
var c = new EventHandlerConstants(); 

var funcReturn = null;  //This will contain a reference to the actual function generated and passed back to 
       //the caller 

var res = { 
     "status" : c.SUCCESS, 
     "actualHandler" : null 
     }; 

if (IsGenuineObject(actualHandler, Function)) 
{ 
    res.actualHandler = function(event) { 

     var trueArgs = [event].concat(args); 

     actualHandler.apply(selfObject, trueArgs); 

    }; 

} 
else 
{ 
    res.status = c.NOTFUNCTION; 

//End if/else 
} 

//Return our result object with appropriate properties set ... 

return(res); 

//End function 
} 

それから私が意図したとおり、これは働いていたかどうかを確認する簡単なテストページを書いた、と私は意志でイベントハンドラを追加したり削除することができました。

次のようにHTMLのテストページがある:

完全性については
<!DOCTYPE html> 
<html> 
<head> 

<!-- CSS goes here --> 

<link rel="stylesheet" type="text/css" href="NewEventTest.css"> 

<!-- Required JavaScript library files --> 

<script language = "JavaScript" src="BasicSupport.js"></script> 
<script language = "JavaScript" src="EventHandler6.js"></script> 

</head> 

<body class="StdC" id="MainApplication"> 

<button type="button" class="StdC NoSwipe" id="Button1">Try Me Out</button> 

<button type="button" class="StdC NoSwipe" id="Button2">Alter The 1st Button</button> 

</body> 

<script language = "JavaScript" src="NewEventTest.js"></script> 

</html> 

、私も次のような単純なCSSファイルを使用して次のように

/* NewEventTest.css */ 


/* Define standard display settings classes for a range of HTML elements */ 

.StdC { 

color: rgba(255, 255, 255, 1); 
background-color: rgba(0, 128, 0, 1); 
font-family: "Book Antiqua", "Times New Roman", "Times", serif; 
font-size: 100%; 
font-weight: normal; 
text-align: center; 

} 


.NoSwipe { 

user-select: none; /* Stops text from being selectable! */ 

} 

テストコードは次のとおりです。

//NewEventTest.js 


function GlobalVariables() 
{ 
this.TmpRef1 = null; 
this.TmpRef2 = null; 
this.TmpRef3 = null; 

this.Const1 = null; 

this.Handler1 = null; 
this.Handler2 = null; 
this.Handler3 = null; 

this.EventOptions = {"passive" : true, "capture" : true }; 

//End constructor 
} 


//Button 1 Initial function 

function Button1Initial(event) 
{ 
console.log("Button 1 initial event handler triggered"); 

//End event handler 
} 


function Button1Final(event) 
{ 
console.log("Button 1 final event handler triggered"); 

//End event handler 
} 


function Button2Handler(event, oldFunc, newFunc) 
{ 
var funcRef = null; 

this.removeEventListener("click", oldFunc); 
this.addEventListener("click", newFunc, GLOBALS.EventOptions); 

//End event handler 
} 


//Application Setup 

GLOBALS = new GlobalVariables(); 

GLOBALS.Const1 = new EventHandlerConstants(); 

GLOBALS.TmpRef1 = document.getElementById("Button1"); 
GLOBALS.TmpRef2 = MakeEventHandler(Button1Initial, null, []); 
if (GLOBALS.TmpRef2.status == GLOBALS.Const1.SUCCESS) 
{ 
    GLOBALS.Handler1 = GLOBALS.TmpRef2.actualHandler; 
    GLOBALS.TmpRef1.addEventListener("click", GLOBALS.Handler1, GLOBALS.EventOptions); 

//End if 
} 

GLOBALS.TmpRef1 = MakeEventHandler(Button1Final, null, []); 
if (GLOBALS.TmpRef1.status == GLOBALS.Const1.SUCCESS) 
{ 
    GLOBALS.Handler3 = GLOBALS.TmpRef1.actualHandler; 

//End if 
} 


GLOBALS.TmpRef1 = document.getElementById("Button2"); 
GLOBALS.TmpRef2 = document.getElementById("Button1"); 
GLOBALS.TmpRef3 = Button1Final; 
GLOBALS.TmpRef4 = MakeEventHandler(Button2Handler, GLOBALS.TmpRef2, [GLOBALS.Handler1, GLOBALS.Handler3]); 
if (GLOBALS.TmpRef4.status == GLOBALS.Const1.SUCCESS) 
{ 
    GLOBALS.Handler2 = GLOBALS.TmpRef4.actualHandler; 
    GLOBALS.TmpRef1.addEventListener("click", GLOBALS.Handler2, GLOBALS.EventOptions); 

//End if 
} 

したがって、実行するテストは次のとおりです。

[1] Button#1にクリックイベントハンドラをアタッチします。

[2]ボタンをクリックしたときにイベントハンドラが呼び出されるかどうかをテストします。

[3]テストに合格したら、ボタン#2をクリックし、それに添付されているイベントハンドラを起動します。ボタン#1に付いている古いイベントハンドラが削除され、新しいイベントハンドラに置き換えられます。

[1]と[2]の手順は問題ありません。イベントハンドラは添付され、ボタンをクリックするたびに呼び出されます。

問題は手順[3]です。

特にMakeEventHandler()によって生成された関数への参照を保存しますが、特にステップ[3]でそのイベントリスナーを削除する目的でremoveEventListener()を呼び出すとイベントリスナーは削除されません。ボタン#1を次々にクリックすると、私はおそらく削除されたイベントリスナを含むイベントリスナを起動します。

言うまでもなく、私は機能は、私はにremoveEventListener(への呼び出しで指定したように、慎重にすべてをセットアップするにもかかわらず、不可解見つけるこの動作は)私は、addEventListenerを(と最初に添加した自己同じ機能です) - によると、すべての私が読んだことのある文書(このスレッドを含む)は、それぞれの呼び出しに対して同じ関数への参照を渡します。でなければなりませんが、明らかにそうではありません。そして、ステップ

予想通り[1]、コンソールのテスト出力は、読み出し:

ボタン1初期イベントハンドラは、コードが期待どおりにもステップで、実行され

をトリガ[2]、コードのステップバイステップトレースは、実際にコードが期待通りに実行されることを示しています。

しかし、ステップ[3]、ボタンの#1の最初クリックで目的の結果得ながら:

ボタン1最後のイベントハンドラをするときのボタン#1、何が起こるか

をトリガーをクリックした後、は、

ボタン1の初期イベントハンドラtr ボタン1最後のイベントハンドラは、最初はボタン#1に添付機能はまだそれがクロージャ内で生成されたので、それはまだのイベントリスナーのコレクションから切り離されるべき、メモリ内に存続する場合でも、確かに

をトリガーiggered要素?なぜそれはまだ接続されていますか?

または、イベントリスナーでクロージャーを使用する際に奇妙なバグが発生しました。報告する必要がありますか?

+1

新しい質問をする必要があります。このエリアはOPの質問に答えます。 – VectorVortec

関連する問題