繁体   English   中英

JavaScript 中匿名函数的 removeEventListener

[英]removeEventListener on anonymous functions in JavaScript

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

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.disable = function() {
    window.document.removeEventListener("keydown", this, false);
}

为什么我不能这样做?

还有其他(好的)方法可以做到这一点吗?

奖金信息; 这只需要在 Safari 中工作,因此缺少 IE 支持。

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

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

编辑:如果您在严格模式下工作,这将不起作用( "use strict";

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

如果我是你,我会创建一个命名函数,或者把它放在一个变量中,这样你就可以引用它。

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

在严格模式下工作的Otto Nascarella解决方案的一个版本是:

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

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

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

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters

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

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

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

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

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

克隆一个节点会复制它的所有属性和它们的值,包括内在(在线)监听器。 它不会复制使用 addEventListener() 添加的事件侦听器

Node.cloneNode()

一个不那么匿名的选择

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>

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

顺便说一句:在设置监听器后立即删除脚本元素是一个隐藏代码的可爱技巧

所以方法(更简单)是:

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

除了 Safari 之外,大多数流行浏览器的最新版本都支持这种新方法。

检查caniuse以获得更新的支持。

更新:现在 Sefari(版本 15^)也支持。

我们可以向addEventListner添加一个名为signal的选项,并从AbortController分配一个signal ,稍后您可以在该信号上调用abort()方法。

这是一个例子。

我们创建一个AbortController

const controller = new AbortController();

然后我们创建eventListner并传入选项signal

document.addEventListener('scroll',()=>{
    // do something
},{signal: controller.signal})

然后在稍后删除eventListner ,我们调用:

controller.abort()

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

事件目标。 addEventListener ( event_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);

我可以用吗

要对此提供更新的方法:

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

我偶然发现了同样的问题,这是我能得到的最好的解决方案:

/*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'事件,因此这种方法可能存在一些问题。

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

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

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

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

然后删除监听器:

target.removeEventListener('click', myFunc);

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

我知道这是一个相当古老的线程,但我想我可能会为那些觉得它有用的人投入两分钱。

脚本(对没有创意的方法名称表示歉意):

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

我刚刚遇到了与版权保护 wordpress 插件类似的问题。 代码是:

function disableSelection(target){
 if (typeof target.onselectstart!="undefined") //For IE 
  target.onselectstart=function(){return false}
 else if (typeof target.style.MozUserSelect!="undefined") //For Firefox
  target.style.MozUserSelect="none"
 else //All other route (For Opera)
  target.onmousedown=function(){return false}
target.style.cursor = "default"
}

然后它是由松散地发起的

<script type="text/javascript">disableSelection(document.body)</script>.

我只是通过将其他匿名函数附加到此事件来解决这个问题:

document.body.onselectstart = function() { return true; };

设置匿名监听器:

document.getElementById('ID').addEventListener('click', () => { alert('Hi'); });

删除匿名侦听器:

document.getElementById('ID').removeEventListener('click',getEventListeners(document.getElementById('ID')).click[0].listener)

使用 AbortController,整洁干净

附加事件监听器

const el = document.getElementById('ID')
const controller = new AbortController;
el.addEventListener('click',() => {
  console.log("Clicked")
},{signal: controller.signal})

当你想删除事件监听器时

controller.abort()

实现此目的的另一种替代解决方法是添加一个空事件处理程序并防止事件传播。

假设您需要从具有#specific-div id 的元素中删除mouseleave事件处理程序,该元素添加了一个匿名函数,并且您不能使用removeEventListener()因为您没有函数名称。

您可以向该元素添加另一个事件处理程序并使用event.stopImmediatePropagation() ,为了确保此事件处理程序在现有事件处理程序之前工作,您应该将第三个参数 ( useCapture ) 传递为true

最终代码应如下所示:

document.getElementById("specific-div")
   .addEventListener("mouseleave", function(event) {
      event.stopImmediatePropagation()
   }, true);

这可能有助于某些您不喜欢cloneNode()方法的特定情况。

window.document.onkeydown = function(){};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM