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

抽屉重复打开时事件触发次数递增问题成因及解决方法

问题产生原因

核心问题是事件监听器重复绑定且未被销毁:

  • 监听.heading-test点击的逻辑写在了showmodal函数内部,每次调用函数打开抽屉,都会往document上新增一个完全独立的点击事件回调。
  • 关闭抽屉时仅用innerHTML = ''删除了DOM节点,这个操作不会清除之前绑定在document上的事件监听器,所有历史绑定的回调都会一直驻留在内存中。
  • 页面触发点击事件时,所有绑定过的符合条件的回调会依次执行,所以每多打开一次抽屉,点击标题时控制台的打印次数就会+1,触发次数和抽屉累计打开次数完全对应。
  • 另外两个监听打开按钮、关闭按钮的逻辑写在函数最外层,页面加载时只会绑定1次,因此不会出现重复触发的问题。
修复方案

方案1(推荐):将标题点击监听移到函数外,仅初始化绑定1次

原有写法本身用的是全局事件委托逻辑,不需要在每次生成DOM的时候重复绑定,页面加载时绑定一次就可以覆盖所有动态生成的.heading-test元素,性能最优。
修复后完整JS代码如下,原有CSS、HTML代码无需改动:

const test_modal = document.querySelector('.test-modal');

// 打开按钮监听:页面初始化仅绑定1次
document.addEventListener('click', (e) => {
    if(e.target.classList.contains('btn')) {
        e.preventDefault();
        const value = e.target.closest('.btn').getAttribute("data-name");
        showmodal(value);
    }
})

// 标题点击监听:移到函数外,页面初始化仅绑定1次
document.addEventListener('click', (e) => {
    if(e.target.classList.contains('heading-test')) {
        console.log(e.target);
    }
})

// 关闭按钮监听:页面初始化仅绑定1次
document.addEventListener('click', (e) => {
    if(e.target.classList.contains('closeme')) {
        test_modal.innerHTML = ``;
    }
})

function showmodal(value) {
    test_modal.innerHTML = `
        <div class="inner-test"><h3 class="heading-test">${value}</h3><button class="closeme">Close</button></div>
    `;
}

方案2:函数内绑定前先移除旧监听器

如果业务场景必须把绑定逻辑写在showmodal内部,注意不能用匿名函数作为回调(移除事件需要传入和绑定时完全一致的函数引用),需要把回调抽为命名函数,每次绑定前先移除旧的同引用监听器:

// 抽离命名回调函数
function headingClickHandler(e) {
    if(e.target.classList.contains('heading-test')) {
        console.log(e.target);
    }
}

function showmodal(value) {
    test_modal.innerHTML = `
        <div class="inner-test"><h3 class="heading-test">${value}</h3><button class="closeme">Close</button></div>
    `;
    // 绑定前先清除之前可能存在的同引用监听器
    document.removeEventListener('click', headingClickHandler);
    document.addEventListener('click', headingClickHandler);
}

方案3:直接给动态生成的元素绑定事件

不使用全局委托,在生成抽屉DOM后,直接给当前的标题元素绑定事件,关闭抽屉用innerHTML清空内容时,DOM节点被移除,对应的监听器也会被自动销毁,不会残留:

function showmodal(value) {
    test_modal.innerHTML = `
        <div class="inner-test"><h3 class="heading-test">${value}</h3><button class="closeme">Close</button></div>
    `;
    // 直接给当前生成的标题节点绑定事件
    test_modal.querySelector('.heading-test').addEventListener('click', (e) => {
        console.log(e.target);
    })
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:24:16