説明が必要な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要素?なぜそれはまだ接続されていますか?
または、イベントリスナーでクロージャーを使用する際に奇妙なバグが発生しました。報告する必要がありますか?
ありがとうございました! – Woppi
+1真実。 'bind(this)'は署名を変更します。したがって、関数 'bind' APIを使用して' this'をバインドした後、常に 'var'に関数を代入し、同じ' var'を 'removeListener'で使用できるようにしてください。あなたはこの問題をよりよく現われています。 – Nirus
これは、関数のパラメータを渡すことを許可しません。 'foo(1)' – Herrgott