JavaScript中匿名函数上的removeEventListener


101

我有一个包含方法的对象。这些方法被放入匿名函数内部的对象中。看起来像这样:

var t = {};
window.document.addEventListener("keydown", function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
});  

(还有很多代码,但这足以显示问题)

现在,在某些情况下,我想停止事件监听器。因此,我试图做一个removeEventListener,但我不知道如何去做。我已经读过其他问题,无法在匿名函数上调用removeEventListener,但是在这种情况下也是如此吗?

我在匿名函数内部创建了一个t方法,因此我认为这是可能的。看起来像这样:

t.disable = function() {
    window.document.removeEventListener("keydown", this, false);
}

我为什么不能这样做?

还有其他(好的)方法吗?

奖金信息;这仅在Safari中有效,因此缺少IE支持。


为什么不保存此功能?事件处理程序可能不是匿名函数。
kirilloid 2011年

2
我意识到这有点晚了,但是您也可以使用Node.setUserData /Node.getUserData方法来存储有关元素的数据。例如,当您需要设置匿名监听器(并能够将其删除)时,首先将userdata设置为匿名函数(Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);,然后执行Elem.addEventListener('event',Elem.getUserData('eventListener'),false);。...和removeEventListener相同。希望你能看到这个没事。
大通

编辑:根据以前的评论,我想这只能在Firefox中使用...我刚刚尝试了IE8(IE9未知),Safari 5.1.2,Chrome(?),Opera11。没有骰子
追赶

Answers:


76

我认为这是匿名函数的重点,它缺少名称或引用它的方式。

如果您是我,则可以创建一个命名函数,或将其放在变量中,以便对其进行引用。

var t = {};
var handler = function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
};
window.document.addEventListener("keydown", handler);

您可以通过以下方式将其删除

window.document.removeEventListener("keydown", handler);   

3
谢谢您的回复。我去了:var handler; window.document.addEventListener(“ keydown”,handler = function(e){但是我不明白的是为什么“ this”没有引用事件监听器。事件监听器不应该是一个对象吗?
bitkid

1
this关键字可能会造成混淆。quirksmode.org/js/this.html
Adam Heath

非常感谢你。这是最有帮助的。
bitkid 2011年

我正在尝试这样做,以阻止网站上真正持久的广告。我知道这是匿名函数的重点,但这并不意味着我不想知道如何做到这一点。
Wyatt8740 '18

@bitkid:在处理函数中(假设它不是箭头函数),this指向添加了侦听器的元素,而不是事件本身(将是参数e)。因此this === e.currentTarget。阅读developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/…–
chharvey

100

如果您在实际函数内部,则可以使用arguments.callee作为对该函数的引用。如:

button.addEventListener('click', function() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', arguments.callee);
});

编辑: 如果您在严格模式下("use strict";),这将无法工作


2
这很好,因为它保留了匿名函数的优点(不污染名称空间等)。
bompf 2012年

3
在WinJS应用程序中尝试此操作,得到下一个错误:“在严格模式下不允许访问参数对象的'callee'属性”
Valentin Kantor 2012年

1
@ValentinKantor:那是因为代码中有些东西是“严格使用”的;语句,则不能在严格模式下使用被调用方。
OMA 2013年

19
给内联函数起一个名字,您可以引用它而无需使用arguments.callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
哈里·洛夫

4
如Mozilla中所述:“警告:第五版ECMAScript(ES5)禁止在严格模式下使用arguments.callee()。​​通过给函数表达式命名或在函数中使用函数声明来避免使用arguments.callee()必须自称。”
dude

50

可在严格模式下使用的Otto Nascarella解决方案的版本是:

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});

4
美丽的解决方案!
埃里克·诺克罗斯

2
这可能不是正确的方法,但这是最简单的方法。
Vignesh '18年

7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

可能是几个匿名函数,keydown 1

警告:仅适用于Chrome Dev Tools&不能在代码中使用链接


2
谢谢,您解决了一个难题,至少在Chrome中解决了这个难题,因为许多笑话者说这是不可能的。伙计,你就像...蝙蝠侠!
JasonXA 2015年

20
getEventListeners似乎是Chrome开发工具的一部分,因此除调试外,它实际上并没有其他用途。
星展银行

1
刚刚尝试过,确认它仅在Devtools中可用,当包含在页面内的脚本中时不可用。
安德烈斯·里奥弗里奥

5

在现代浏览器中,您可以执行以下操作...

button.addEventListener( 'click', () => {
    alert( 'only once!' );
}, { once: true } );

https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener#Parameters


很酷,直到您发现IE版本和Edge <16都没有实际支持此功能。从那时起,至少在5年内我们可以使用它,然后IE将被弃用(阅读:应该),Edge将取代它,它将使用webkit引擎而不是他们自己的“ EdgeHTML”东西。
SidOfc

1
使用这个用于DOM Level 4条目的polyfill,
shunryu111 '19

2

一个不太匿名的选择

element.funky = function() {
    console.log("Click!");
};
element.funky.type = "click";
element.funky.capt = false;
element.addEventListener(element.funky.type, element.funky, element.funky.capt);
// blah blah blah
element.removeEventListener(element.funky.type, element.funky, element.funky.capt);

由于收到了Andy的反馈很正确,但与许多示例一样,我希望显示该想法的上下文扩展),因此这里的说明较为简单

<script id="konami" type="text/javascript" async>
    var konami = {
        ptrn: "38,38,40,40,37,39,37,39,66,65",
        kl: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
    };
    document.body.addEventListener( "keyup", function knm ( evt ) {
        konami.kl = konami.kl.slice( -9 );
        konami.kl.push( evt.keyCode );
        if ( konami.ptrn === konami.kl.join() ) {
            evt.target.removeEventListener( "keyup", knm, false );

            /* Although at this point we wish to remove a listener
               we could easily have had multiple "keyup" listeners
               each triggering different functions, so we MUST
               say which function we no longer wish to trigger
               rather than which listener we wish to remove.

               Normal scoping will apply to where we can mention this function
               and thus, where we can remove the listener set to trigger it. */

            document.body.classList.add( "konami" );
        }
    }, false );
    document.body.removeChild( document.getElementById( "konami" ) );
</script>

这允许有效的匿名函数结构,避免使用实际上不推荐使用的被调用方,并允许轻松删除。

顺便说一句:设置侦听器后立即删除script元素是一种隐藏代码的可爱技巧,人们宁愿撬起眼睛也不是很明显(会破坏惊喜;-)

因此,方法(更简单地说)是:

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );

2
这太复杂了。
Ben Sinclair 2014年

@Andy,我同意,但试图显示根本没有办法删除匿名函数。它必须以某种方式加以引用(即使被叫方(是坏的,M'Kay)被引用的功能),从而提供的只有一个(其他)方式的例子中,函数可以被引用的-而且,它是如何建成的部分的那同样可以存储以供以后参考(重要部分)。显然,真正的匿名函数是动态生成的,因此以后还必须知道哪个事件操作/类型以及是否使用捕获。无论如何,这是一个更好的方法:-)
Fred Gandt 2014年

为我完美地工作。我看不到将参数传递给函数的另一种方法,因为它不能匿名。
nicodemus13 2014年

2

这是不理想的,因为它会删除所有内容,但可能会满足您的需求:

z = document.querySelector('video');
z.parentNode.replaceChild(z.cloneNode(1), z);

克隆节点会复制其所有属性及其值,包括固有(串联)侦听器。它不会复制使用addEventListener()添加的事件侦听器

Node.cloneNode()


这绝对是绝妙的
艾哈迈德·阿尔菲

1

JavaScript:addEventListener 方法将指定的侦听器注册在调用它的EventTarget(Element | document | Window)上。

EventTarget。addEventListenerevent_type,handler_function,Bubbling | Capturing);

鼠标,键盘事件 WebConsole中的示例测试:

var keyboard = function(e) {
    console.log('Key_Down Code : ' + e.keyCode);
};
var mouseSimple = function(e) {
    var element = e.srcElement || e.target;
    var tagName = element.tagName || element.relatedTarget;
    console.log('Mouse Over TagName : ' + tagName);    
};
var  mouseComplex = function(e) {
    console.log('Mouse Click Code : ' + e.button);
} 

window.document.addEventListener('keydown',   keyboard,      false);
window.document.addEventListener('mouseover', mouseSimple,   false);
window.document.addEventListener('click',     mouseComplex,  false);

removeEventListener 方法将删除先前向EventTarget.addEventListener()注册的事件侦听

window.document.removeEventListener('keydown',   keyboard,     false);
window.document.removeEventListener('mouseover', mouseSimple,  false);
window.document.removeEventListener('click',     mouseComplex, false);

我可以用吗


1

要对此提供最新的方法:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);

2
一个解释会很好。
Poul Bak

1

我偶然发现了相同的问题,这是我可以获得的最佳解决方案:

/*Adding the event listener (the 'mousemove' event, in this specific case)*/
element.onmousemove = function(event) {
    /*do your stuff*/
};
/*Removing the event listener*/
element.onmousemove = null;

请记住,我仅针对window元素和'mousemove'事件对此进行了测试,因此这种方法可能会出现一些问题。


0

就您的要求而言,可能不是最佳的解决方案。我还没有确定一种有效的方法来删除与事件侦听器调用内联声明的匿名函数。

我个人使用变量来存储<target>和在事件侦听器调用之外声明函数,例如:

const target = document.querySelector('<identifier>');

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

然后删除监听器:

target.removeEventListener('click', myFunc);

不是您会收到的最高建议,而是删除匿名函数,我发现唯一有用的解决方案是删除然后替换HTML元素。我敢肯定必须有一个更好的香草JS方法,但我还没有看到它。


0

我知道这是一个相当老的话题,但我想我可能会为那些觉得有用的人投入两美分。

脚本(关于非创意方法名称的道歉):

window.Listener = {
    _Active: [],
    remove: function(attached, on, callback, capture){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            if(current[0] === attached && current[1] === on && current[2] === callback){
                attached.removeEventListener(on, callback, (capture || false));
                return this._Active.splice(i, 1);
            }
        }
    }, removeAtIndex(i){
        if(this._Active[i]){
            var remove = this._Active[i];
            var attached = remove[0], on = remove[1], callback = remove[2];
            attached.removeEventListener(on, callback, false);
            return this._Active.splice(i, 1);
        }
    }, purge: function(){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            current[0].removeEventListener(current[1], current[2]);
            this._Active.splice(i, 1);
        }
    }, declare: function(attached, on, callback, capture){
        attached.addEventListener(on, callback, (capture || false));
        if(this._Active.push([attached, on, callback])){
            return this._Active.length - 1;
        }
    }
};

您可以像这样使用它:

// declare a new onclick listener attached to the document
var clickListener = Listener.declare(document, "click" function(e){
    // on click, remove the listener and log the clicked element
    console.log(e.target);
    Listener.removeAtIndex(clickListener);
});

// completely remove all active listeners 
// (at least, ones declared via the Listener object)
Listener.purge();

// works exactly like removeEventListener
Listener.remove(element, on, callback);

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.