web-dev-qa-db-ja.com

Javascript removeEventListenerが機能しない

EventListenerを追加する次のコードがあります

 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);

しかし、偶数リスナーは削除されません。なぜ発生するのですか?removeEventListener()に問題はありますか?注:ここの領域はdocument.getElementById( 'myId')のようなものです

52

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

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

両方の呼び出しで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);
5
Sirko

Windowsオブジェクトには、最後のパラメーター「true」が必要であることがわかりました。キャプチャフラグがない場合、削除は機能しません。

1
Slavik

削除するには、関数を変数に保存するか、単に名前付き関数を使用して、その関数をremoveEventListener呼び出しに渡します。

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

area.addEventListener('click', areaClicked, true);
// ...
area.removeEventListener('click', areaClicked, true);
1
ThiefMaster

イベントリスナによって呼び出される関数にローカル変数を渡したい場合は、関数内で関数を定義して(ローカル変数を取得する)、関数自体に関数の名前を渡すことができます。たとえば、ローカル変数として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);
}
1
VectorVortec

最初にイベントハンドラを定義し、

その後

area.addEventListener('click',handler);
area.removeEventListener('click',handler);
0
neohope

説明が必要な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]ボタン#1にクリックイベントハンドラーを接続します。

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

[3]そのテストに合格したら、ボタン#2をクリックして、そこにアタッチされたイベントハンドラーを呼び出します。これにより、ボタン#1にアタッチされた古いイベントハンドラーが削除され、新しいイベントハンドラーに置き換えられます。

手順[1]および[2]は正常に機能します。イベントハンドラがアタッチされ、ボタンをクリックするたびに呼び出されます。

問題はステップ[3]にあります。

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

言うまでもなく、removeEventListener()の呼び出しで指定する関数がaddEventListener()で最初に追加した関数と同じになるようにすべてを慎重に設定しているにもかかわらず、この動作は不可解です。all私が読んだテーマ(このスレッドを含む)のドキュメント。各呼び出しで同じ関数への参照を渡しますshould動作しますが、明らかに動作しません。

ステップ[1]で、期待どおりコンソールのテスト出力が読み取ります。

ボタン1の初期イベントハンドラーがトリガーされた

コードは期待どおりにステップ[2]でも実行され、コードのステップバイステップトレースは、実際にコードが期待どおりに実行されることを明らかにします。

しかし、ステップ[3]では、firstボタン#1をクリックすると、望ましい結果が得られます。

ボタン1の最終イベントハンドラーがトリガーされた

ボタン#1がクリックされると、続いてがどうなりますか?

ボタン1の初期イベントハンドラーのトリガーボタン1の最終イベントハンドラーのトリガー

確かに、最初にButton#1にアタッチされた関数がクロージャー内で生成されたためにメモリに残っている場合でも、要素のイベントリスナーコレクションからデタッチする必要がありますか?なぜまだ接続されているのですか?

または、イベントリスナーでクロージャを使用することに関する奇妙なバグに遭遇したことがありますか?

0
David Edwards