【问题标题】:Javascript removeEventListener not workingJavascript removeEventListener 不工作
【发布时间】:2022-12-31 07:46:56
【问题描述】:

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

但是 even listener 并没有被移除..为什么会这样?我的 removeEventListener() 有什么问题吗? 注意:这里的区域类似于 document.getElementById('myId')

【问题讨论】:

标签: javascript events addeventlistener


【解决方案1】:

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

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

【讨论】:

  • +1 是的。 bind(this) 将更改签名。因此,在将this 绑定到使用函数bind API 之后,始终将函数分配给var,以便可以在removeListener 中使用相同的var。你会在打字稿中看到这个问题更明显
  • 那将不允许您传递函数参数 f.e. foo(1)
  • 如果有人使用类,请在任何侦听器之前尝试this.onClick = this.onClick.bind(this),然后是btn.addEventListener('click', this.onClick),最后是btn.removeEventListener('click', this.onClick)
  • @Herrgott 要将参数传递给处理函数,您可以使用柯里化:foo = (argumentToPass) => (event) => { doSomething(); },然后是xyz.addEventListener('click', foo('myarg'), true);。 foo('myarg') 将返回另一个将 argumentToPass 设置为 myarg 的函数。请记住在实际代码中保持对 fn 的引用 :-)
  • @joseluisq 你能解释一下 bind(this) 是什么意思吗?它有效,但我不知道为什么
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    在 React 函数组件中,确保使用 useCallback(() => {}) 钩子定义回调。如果你不这样做,回调将在每次重新渲染时都是不同的,并且 removeEventListener 方法将不起作用。

    const scrollCallback = useCallback(() => { // do sth. });
    window.addEventListener("scroll", scrollCallback, true);
    window.removeEventListener("scroll", scrollCallback, true);
    

    【讨论】:

    • 这救了我的命,我认为它是 const 所以它不会在重新渲染时重新声明但是......
    • React 不使用显式事件侦听器,所以感觉这与实际问题帖子没有任何关系?
    【解决方案4】:

    您正在两个调用中创建两个不同的函数。所以第二个函数与第一个函数没有任何关系,引擎可以删除该函数。改用函数的通用标识符。

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

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

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

    【讨论】:

      【解决方案5】:

      看起来没有人涵盖 JavaScript 规范的一部分,它现在为您提供了一种删除事件侦听器的机制没有使用removeEventListener。如果我们查看https://dom.spec.whatwg.org/#concept-event-listener,我们会发现在设置事件侦听器时有许多属性可以作为选项传递:

      {
          type (a string)
          callback (an EventListener object, null by default)
          capture (a boolean, false by default)
          passive (a boolean, false by default)
          once (a boolean, false by default)
          signal (an AbortSignal object, null by default)
          removed (a boolean for bookkeeping purposes, false by default)
      }
      

      现在,该列表中有很多有用的属性,但为了删除事件侦听器,我们要使用的是 signal 属性(已添加到 DOM 级别 3 in late 2020),因为它让我们告诉 JS 引擎通过使用 AbortController 删除事件侦听器,而不必费心保留对确切处理函数和侦听器选项的引用“因为否则 removeEventListener 甚至无法正常工作”:

      const areaListener = new AbortController();
      
      area.addEventListener(
        `click`,
        ({clientX: x, clientY: y}) => {
          app.addSpot(x, y);
          app.addFlag = 1;
        },
        areaListener
      );
      

      (我们利用了 areaListener 等同于 { signal: ... } 对象这一事实,在任何其他事件侦听器选项属性中没有重叠)

      现在,当需要删除该事件侦听器时,我们只需运行:

      areaListener.abort()
      

      完成:JS 引擎将中止并清理我们的事件侦听器。不保留对处理函数的引用,不确保我们调用 removeEventListener 时使用与我们调用 addEventListener 时完全相同的功能和属性:我们只是用一个无参数的中止调用取消监听器。

      当然,还要注意,如果我们想这样做“因为我们只希望处理程序触发一次”,那么我们甚至不需要这样做, 我们可以只用 { once: true } 创建一个事件监听器,JS 会处理剩下的事情。无需删除代码。

      area.addEventListener(
        `click`,
        () => app.bootstrapSomething(),
        { once: true }
      );
      

      【讨论】:

      • 好资料。很高兴在问题日期 9 年后看到答案。
      • JS 总是在变化,有些话题值得跟进:几年前唯一的解决方案和正确答案在今天可能已经完全过时了,这里肯定是这种情况(尤其是 IE 只剩下几个月的生命了对于整个产品线。最后 =)
      • 我今天学了些新东西。如果这真的解决了我的问题,我会报告。
      • 您必须在控制器上调用 abort 方法(而不是在信号对象本身上)
      • 确实,愚蠢的错误,代码已更新。
      【解决方案6】:

      要删除它,将函数存储在一个变量中或简单地使用一个命名函数并将该函数传递给 removeEventListener 调用:

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

      【讨论】:

      • 但是我如何将参数(这里是事件)传递给那个函数..这就是我使用匿名函数的原因
      • 它由浏览器传递。是否单独定义函数并不重要。
      • 警告:我发现我的方法有什么问题。 removeEventListener() 方法仅适用于命名函数。它不适用于匿名函数!当我编辑代码以考虑到这一点时,一切都按计划进行。您必须在闭包中定义一个 NAMED 函数,并使用闭包传递的参数返回对其实例的引用。这样做,removeEventListener() 就会完美地工作。
      【解决方案7】:

      如果要将局部变量传递给事件侦听器调用的函数,可以在函数内部定义函数(以获取局部变量)并在函数本身中传递函数名称。例如,让我们从添加带有 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(),该函数也不会被删除。评论太小,无法发布代码,所以如果你想要代码,我将不得不用完一个答案框......
      • 上面的附录:我发现的另一个有趣的现象是,即使您指定您的事件侦听器是被动的,旧的仍然存在于链中。更糟糕的是,旧的现在变成了阻塞事件处理程序,而新的则保持其被动状态。我认为这里需要一个解释。
      【解决方案8】:

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

      接着

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

      【讨论】:

      • 对于未来的 visotirs:我们曾经需要这个,但是 2020 年的 JS 有一个中止信号,您可以改用它,所以不再需要 you don't need removeEventListener。
      【解决方案9】:

      这是我最终做的,但它在路由类中,但应该不会有太大区别,我希望事件监听器不会在每次调用 afterModel() 时累积,但还需要参数和范围,以便每次都更改模型.

      export default class iFrameRoute extends Route {
      
            afterModel(model) {
      
                 this.initFrame = function(event) {  
          
                     alert("I am being called");
      
                     window.removeEventListener("message",  this.route.test); 
      
                 }.bind({route: this, data: model});
      
                 window.addEventListener("message",  this.initFrame ); 
             } 
      }
      

      【讨论】:

        【解决方案10】:

        我最近遇到了同样的问题。我找到的一个合理的解决方案是从 HTMLElement 类中删除元素上的属性“onclick”。

        假设您已经从 DOM 中获取组件 - 使用文档.getElementById要么文档查询选择器- 你可以试试那个代码:

        js

        const element = document.getElementById("myelement");
        element.attributes.removeNamedItem('onclick');
        

        例子

        <div onClick="memoryGame.flipCard(this)">
           .... // children elements
        </div>
        

        我知道这不是最好的解决方案,但它确实有效!

        我希望我能帮助你。

        干杯!

        PS:请给我一个“有用的答案”......谢谢:D

        【讨论】:

        • 很高兴看到即使在 10 年后这个线程仍然活跃。
        • @JinuJosephDaniel 那是真男人。我们总是可以从另一个角度学习新东西或改进。这个解决方案对你有好处吗?!?
        • 虽然不确定这是一个合理的解决方案,因为它依赖于一种不应再使用的事件侦听器。只需使用“新”中止信号 =)
        【解决方案11】:

        同时在数组中添加函数存储并为我删除通过地图工作

        const [functionObjects, setfunctionObjects] = useState([]);
        
        const addListener = (beforeUnloadListener) =>{ 
            setfunctionObjects([...nano, beforeUnloadListener]);
            addEventListener("beforeunload", beforeUnloadListener, {capture: true});
        };
        
        const removeListener = (beforeUnloadListener) => { 
            functionObjects.map((item) => {
            removeEventListener("beforeunload", item, {capture: true});});
        };
        

        【讨论】:

        • 这是 React 代码,对于一个问题不是关于反应。
        【解决方案12】:

        在反应的情况下我们可以用使用引用()将我们的侦听器函数存储在当前属性中。因此,在重新渲染和删除侦听器的情况下,它将保持对该函数的相同引用。

        const handleWindowClick = useRef(() => {
            console.log("window clicked");
          });
        
        
        
        // for attaching event listener
         window.addEventListener("click", handleWindowClick.current);
         
         // for detaching event listener
         window.removeEventListener("click", handleWindowClick.current);
        

        【讨论】:

          猜你喜欢
          • 2011-08-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-14
          • 1970-01-01
          • 2018-03-24
          相关资源
          最近更新 更多