You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用setTimeout结合类方法无法移除元素事件监听器的问题

问题

我实现了App类的btnInteractive方法,该方法根据传入的布尔值,为.btn--start元素添加或移除mouseover和mouseout事件监听器。点击按钮触发start方法后,先调用this.btnInteractive(true)添加监听器,2秒后通过setTimeout执行this.btnInteractive(false)尝试移除监听器,但实际监听器并未被移除,鼠标移入移出按钮仍会触发日志输出。

已做的排查操作:

  • 确认if和else代码块均已执行
  • 精简代码至当前版本
  • 移除click事件监听器,无效果
  • 移除click和mouseout事件监听器,仍无效

预期效果:2秒后成功移除mouseover和mouseout事件监听器。

相关代码

'use strict';

const btnStart = document.querySelector('.btn--start');

class App {
  constructor() {
    btnStart.addEventListener('click', this.start.bind(this));
  }

  btnInteractive(state) {
    console.log(`Check: ${state}`);
    function mouseover() {
      console.log('over');
    }

    function mouseout() {
      console.log('out');
    }
    if (state) {
      btnStart.addEventListener('mouseover', mouseover);
      btnStart.addEventListener('mouseout', mouseout);
    } else {
      btnStart.removeEventListener('mouseover', mouseover);
      btnStart.removeEventListener('mouseout', mouseout);
    }
  }

  start() {
    this.btnInteractive(true);
    setTimeout(() => {
      this.btnInteractive(false);
    }, 2000);
  }
}

const app = new App();

控制台输出

Check: true
out
over
Check: false
out
over

解决方案

问题核心:每次调用btnInteractive时,都会创建全新的mouseover和mouseout函数。removeEventListener要求传入的函数引用必须和添加时完全一致,否则无法识别并移除。

修改方案是把事件处理函数挂载到类实例上,确保两次调用btnInteractive时使用同一个函数引用:

'use strict';

const btnStart = document.querySelector('.btn--start');

class App {
  constructor() {
    // 将事件处理函数绑定到实例,确保引用唯一
    this.mouseoverHandler = this.mouseoverHandler.bind(this);
    this.mouseoutHandler = this.mouseoutHandler.bind(this);
    btnStart.addEventListener('click', this.start.bind(this));
  }

  // 独立定义事件处理方法
  mouseoverHandler() {
    console.log('over');
  }

  mouseoutHandler() {
    console.log('out');
  }

  btnInteractive(state) {
    console.log(`Check: ${state}`);
    if (state) {
      btnStart.addEventListener('mouseover', this.mouseoverHandler);
      btnStart.addEventListener('mouseout', this.mouseoutHandler);
    } else {
      btnStart.removeEventListener('mouseover', this.mouseoverHandler);
      btnStart.removeEventListener('mouseout', this.mouseoutHandler);
    }
  }

  start() {
    this.btnInteractive(true);
    setTimeout(() => {
      this.btnInteractive(false);
    }, 2000);
  }
}

const app = new App();

修改后,添加和移除监听器使用的是同一个函数引用,2秒后即可成功移除事件监听器,鼠标移入移出按钮不再触发日志输出。

内容的提问来源于stack exchange,提问作者Pasha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 19:15:57