JavaScript:如何从事件监听器中删除该监听器?

29

我一直想知道这种方法的清洁程度 - 在事件侦听器内部删除事件侦听器。

更新:

在内部,我保留了对象和侦听器的哈希表,因此我可能可以从任何地方删除事件侦听器。 我只是担心从自身内部删除它是否有效?

更新:

我正在询问addEventListener,removeEventListener 等内容。


我们是在讨论 addEventListener 还是 .onxxx = - Roatin Marth
7个回答

63

1
是的...我认为当我询问这个问题的时候,那还没有可用:D - jayarjo
但我认为,仅仅拥有一个命名的事件处理程序,然后在处理程序内部实际上将其删除,是最好的、最符合跨浏览器标准的方法。 - jayarjo
1
此外,在某些应用程序中,一次并不足够——“一次”只是一个特定的情况。 - broc.seib
我认为在2020年对于大多数情况来说,这是最好的方法,因为大多数人想要删除监听器的原因是仅执行一次。然而,在不是单例执行的情况下,这个解决方案并不那么适用。我猜你仍然可以通过设置“once”并在回调函数中添加另一个监听器来使其工作,如果满足任意条件,实质上做一些奇怪的递归,但此时其他解决方案显然更加清晰。 - cyqsimon

18

我之所以看到这个,是因为我也有同样的问题!

arguments.callee 是你的朋友...

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee

那么你会有

blah.addEventListener('click',function(e){
    e.source.removeEventListener('click', arguments.callee);
    blee bloo bleep
});

这在Titanium Appcelerator中可行,所以它应该也能在JavaScript中工作(因为它们有点相同)。
注意,在此示例中不要在arguments.callee末尾添加(),除非你喜欢看到...bah dum tish!
实际上,如果你不想使用arguments.callee,这也可能有效(未经测试)。
blah.addEventListener('click', anyThingYouWantHere = function(e){
    e.source.removeEventListener('click', anyThingYouWantHere);
    blee bloo bleep
});

在这里,“anythingYouWantHere”是您想要的任何变量名称 ~ 当您添加函数时,您实际上正在“命名”该函数。


3
在 ES5 的严格模式下,这是不允许的,对吧? - J. K.
哈!我甚至不知道这种东西的存在。无论如何,我四处查找并找到了这个https://dev59.com/lnVD5IYBdhLWcg3wDXJ3#1335595,它似乎表明它仍然存在。此外,我引用的关于arguments.callee的链接也没有提到它已被弃用。该链接确实提到`()`变体不起作用,但我们在这里没有使用它...因为stackoverflow。 - bharal
你好!你的第二个例子是一个赋值操作,对于使用赋值操作符“=”的JavaScript,它将有效。 - Drozerah
@user,请查看您上面的评论。 - bharal

11

我刚刚编写了一个包装函数,用于生成自毁事件监听器:

let addSelfDestructingEventListener = (element, eventType, callback) => {
    let handler = () => {
        callback();
        element.removeEventListener(eventType, handler);
    };
    element.addEventListener(eventType, handler);
};

到目前为止,它运行得非常好:)


1
这个非常有效,不是所有的浏览器都支持 {once: true}!! 这应该是推荐的答案。 - Peter I

3

@bharal的答案给了我以下解决方案:

//example
addBlurListener(element, field) {
    const listenToBlur = (e) => {
        e.target.removeEventListener(e.type, listenToBlur);
        //your stuff
    };
    element.addEventListener('blur', listenToBlur);
},

1
你可以尝试类似这样的代码,具体根据它的命名而定:
some_div.onclick = function () {
    ...
    this.onclick = null;
    // or: some_div.onclick = null;
};

或者你关心的是事件监听器?因为它们稍微有点复杂。


是的,听众们...我以为处理程序和监听器是一样的,不是吗? - jayarjo
1
@jayarjo 一个元素只能有一个事件处理程序,但可以有任意数量的监听器。这是我的理解,无论如何。 - sdleihssirhc

1
如果您希望监听器只触发一次,可以使用以下代码:

element.addEventListener('eventname', function callback(){}, { once: true }); 

或者使用包装器来完成同样的事情:
function addOneTimeEventListener(element, event, callback) {
    const wrapper = e => {
        try {callback(e)} finally {
            element.removeEventListener(event, wrapper);
        };
    }
    element.addEventListener(event, wrapper);
}

// example
addOneTimeEventListener(document.body, 'click', e => {
  console.log('this message only show once.');
});

如果你想决定何时移除监听器:

function addEventListener(element, event, callback) {
    const wrapper = e => {
        callback(e, () => element.removeEventListener(event, wrapper));
    }
    element.addEventListener(event, wrapper);
}

// example
let count = 0;
addEventListener(document.body, 'click', (e, closeListener) => {
  console.log(`click:${++count}`);
  if(count == 3) closeListener();
});

0
如果你使用jQuery,你可能会发现一些方便的方法用于与事件处理程序交互--参见bind()/unbind(),delegate()/undelegate(),one()和类似的方法。
我对其他框架没有太多经验,但我想象它们提供类似的功能。如果你根本不使用框架,@sdleihssirhc有一个可接受的答案。
编辑:啊,也许你正在寻找更像addEventListener()和removeEventListener()这样的东西。同样,一个框架将为您的交互提供一些便利,并在某些情况下节省您重新发明轮子的麻烦。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接