JavaScript removeEventListener不起作用


78

我有以下代码添加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')


Answers:


125

这是因为两个匿名函数是完全不同的函数。您removeEventListener的参数不是对先前附加的函数对象的引用。

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

1
非常感谢!
Woppi

54
+1正确。bind(this)将更改签名。所以始终分配功能的var结合之后,this使用功能bindAPI,以便同样var可以用在removeListener。您会在打字稿中看到这个问题更加明显
Nirus

4
那将不允许您传递函数参数fefoo(1)
Herrgott

2
感谢不引用以前附加的功能对象
Evhz

16
如果有人使用类尝试this.onClick = this.onClick.bind(this)在任何听众面前尝试类似的东西,那么btn.addEventListener('click', this.onClick),最后btn.removeEventListener('click', this.onClick)
joseluisq,

11

我发现对于Windows对象,最后一个参数“ true”是必需的。如果没有捕获标志,则删除将不起作用。


解决了我的问题。谢谢。
James Manes

7

您将在两个调用中创建两个不同的函数。因此,第二功能与第一功能没有任何关系,并且引擎能够删除该功能。请使用该函数的通用标识符。

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

稍后您可以通过调用删除处理程序

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

1

要删除它,请将函数存储在变量中,或仅使用命名函数并将该函数传递给removeEventListener调用:

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

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

但我怎么能传递参数(在此事件)到function..That就是为什么我用匿名函数
金·约瑟夫丹尼尔

它由浏览器传递。是否单独定义函数都没有关系。
ThiefMaster 2012年

警告:我发现我的方法出了什么问题。removeEventListener()方法仅适用于NAMED FUNCTIONS。它不适用于匿名功能!当我编辑代码以考虑到这一点时,一切都按计划进行。您必须在闭包中定义NAMED函数,并使用闭包传递的参数返回对其实例的引用。这样做,removeEventListener()可以完美地工作。
David Edwards

1

如果要将局部变量传递给事件侦听器调用的函数,则可以在函数内部定义函数(以获取局部变量),然后在函数本身中传递函数名称。例如,让我们开始在添加事件监听器的函数内部,其中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);
}

我在这里遇到了问题。即使您定义了事件处理程序函数,保存了对该函数的引用,然后将该引用传递给removeEventListener(),该函数也不会被删除。注释太小,无法发布代码,因此,如果您想编写代码,我将不得不用完一个答案框...
David Edwards

上面的附录:我发现的另一个有趣的现象是,即使您指定事件监听器是被动的,旧的监听器仍然存在。更糟糕的是,旧的现在变成了阻塞事件处理程序,而新的则保持其被动状态。我认为这里需要解释。
David Edwards

0

首先定义您的事件处理程序,

然后

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

0

我遇到了需要说明的removeEventListener()问题。

我希望能够将参数传递给事件侦听器,因此编写了一个函数来生成事件侦听器,该函数又返回了第二个函数,该函数将我想要的事件侦听器作为回调进行调用。

完整的库文件如下:

//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()最初添加的自同函数,尽管我对此进行了仔细的设置,但我还是感到困惑-根据有关该主题的所有文档已经读过(包括该线程),每次调用传递对同一函数的引用应该可以,但显然不行。

在步骤[1]中,控制台中的测试输出按预期读取:

按钮1初始事件处理程序已触发

该代码还将按预期方式在步骤[2]中运行,并且逐步跟踪该代码可发现,确实,该代码已按预期执行。

但是在步骤[3]中,第一次单击按钮#1会产生所需的结果:

按钮1最终事件处理程序已触发

随后单击按钮#1时会发生以下情况:

按钮1初始事件处理程序已触发按钮1最终事件处理程序已触发

当然,即使最初附加于Button#1的函数仍然保留在内存中,因为它是在闭包中生成的,仍应将其与元素的事件侦听器集合分离吗?为什么仍然连接?

还是我遇到了一些奇怪的错误,其中涉及将闭包与事件侦听器一起使用,需要进行报告?


2
您应该问一个新问题。该区域回答了OP的问题。
VectorVortec

我也遇到了这个问题。废话 不得不诉诸肮脏的方法,即。跟踪应在窗口对象中启用/禁用哪些事件。
Mave
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.