如何在原生JS或Puppeteer中移除window或document的事件监听器
Puppeteer移除事件监听器的实现方案
移除指定mousedown监听器的解决方法
你之前写的占位符代码无法生效的核心原因是:removeEventListener必须传入和绑定时完全一致的三个参数:事件类型、监听函数引用、useCapture配置,且CDP返回的handler是远程对象引用,无法直接通过page.evaluate的序列化传参传入页面上下文。
正确的实现代码如下:
const client = await page.target().createCDPSession() // 获取document的远程对象引用 const { result: docRef } = await client.send('Runtime.evaluate', { expression: 'document', objectGroup: 'eventHandlers' }) // 获取document上绑定的所有监听器 const { listeners } = await client.send('DOMDebugger.getEventListeners', { objectId: docRef.objectId }) // 遍历移除所有mousedown监听器(可以加条件过滤你不需要的) for (const listener of listeners) { if (listener.type !== 'mousedown') continue // 调用Runtime.callFunctionOn直接在页面上下文执行移除操作,支持传入远程对象引用 await client.send('Runtime.callFunctionOn', { // 要执行的函数,this指向传入的objectId对应的对象(这里是document) functionDeclaration: `function(type, listenerFn, useCapture) { this.removeEventListener(type, listenerFn, useCapture) }`, objectId: docRef.objectId, // 三个参数分别对应事件类型、监听函数引用、useCapture配置 arguments: [ { value: listener.type }, { objectId: listener.handler.objectId }, { value: listener.useCapture } ] }) } // 释放对象组,避免内存泄漏 await client.send('Runtime.releaseObjectGroup', { objectGroup: 'eventHandlers' })
移除document上全部事件监听器的方案
如果不需要保留document的原有绑定,有更简单的一键方案:
- 方案1:克隆替换节点(最快捷,适合绝大多数自动化测试场景)
await page.evaluate(() => { // 克隆document节点不会拷贝绑定的事件监听器 document.replaceWith(document.cloneNode(true)) })
- 方案2:遍历所有事件类型移除
await page.evaluate(() => { Object.getOwnPropertyNames(document) .filter(prop => prop.startsWith('on')) .map(prop => prop.slice(2)) .forEach(eventType => { // 同时移除捕获和冒泡阶段的所有监听器 document.removeEventListener(eventType, document[`on${eventType}`], true) document.removeEventListener(eventType, document[`on${eventType}`], false) document[`on${eventType}`] = null }) })
window上绑定的监听器处理方法
和document的处理逻辑完全一致,仅需做少量修改:
- 用CDP获取监听器时,把
expression: 'document'改为expression: 'window'即可,后续移除逻辑不变 - 更适合自动化测试场景的前置拦截方案:
// 在页面加载前就注入代码,拦截所有你不需要的事件绑定 await page.evaluateOnNewDocument(() => { const originalAddEventListener = EventTarget.prototype.addEventListener EventTarget.prototype.addEventListener = function(type, listener, options) { // 拦截window或document上的mousedown事件绑定,直接不执行注册逻辑 if (type === 'mousedown' && (this === window || this === document)) return // 其他事件正常注册 return originalAddEventListener.call(this, type, listener, options) } })
这个方案可以从根源上避免拦截点击的监听器被绑定,不需要后续再做移除操作。
内容的提问来源于stack exchange,提问作者ken
相关产品推荐
相关产品推荐

