如何识别特定事件的源JS文件?解决document.open事件丢失问题
识别事件监听器的源脚本文件,解决
document.open后的事件复用问题 你遇到的核心痛点是没法动态判断addEventListener调用来自哪个脚本文件,之前硬编码的方式显然不够灵活。这里有个可靠的方案,利用JavaScript的调用栈(Error.stack)来提取当前执行代码的源文件信息,完美适配你的需求:
步骤1:添加脚本文件检测辅助函数
首先写一个工具函数,通过创建Error对象读取调用栈,解析出当前执行代码所在的脚本文件名。这个方法兼容绝大多数现代浏览器:
function getCurrentScriptFileName() { try { throw new Error(); } catch (e) { // 拆分调用栈行,跳过当前函数和addEventListener自身的调用记录 const stackLines = e.stack.split('\n'); const targetLine = stackLines.find(line => !line.includes('getCurrentScriptFileName') && !line.includes('addEventListener') ); if (!targetLine) return ''; // 适配不同浏览器的栈格式,提取文件名 const urlMatch = targetLine.match(/(https?:\/\/|file:\/\/).*?(\/[^\/]+?\.js)(?::\d+)?/i); const fileNameMatch = targetLine.match(/([^\/]+?\.js)(?::\d+)?/i); return urlMatch ? urlMatch[2].split('/').pop() : fileNameMatch ? fileNameMatch[1] : ''; } }
步骤2:修改addEventListener逻辑,动态检测源文件
把你代码里硬编码的isFromSourceX替换成调用上述函数的结果,就能自动识别事件监听器是否来自你的目标脚本:
(function(self) { // 初始化全局存储数组(防止重复初始化) if (!window._stackedListeners) { window._stackedListeners = []; } function getCurrentScriptFileName() { try { throw new Error(); } catch (e) { const stackLines = e.stack.split('\n'); const targetLine = stackLines.find(line => !line.includes('getCurrentScriptFileName') && !line.includes('addEventListener') ); if (!targetLine) return ''; const urlMatch = targetLine.match(/(https?:\/\/|file:\/\/).*?(\/[^\/]+?\.js)(?::\d+)?/i); const fileNameMatch = targetLine.match(/([^\/]+?\.js)(?::\d+)?/i); return urlMatch ? urlMatch[2].split('/').pop() : fileNameMatch ? fileNameMatch[1] : ''; } } EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener; EventTarget.prototype.addEventListener = function(type, listener, params) { // 动态获取当前调用者的脚本文件名 const currentScript = getCurrentScriptFileName(); const isFromSourceX = currentScript === 'nameoffile.js'; const worker_events = ['DOMContentLoaded', 'beforeunload', "blue","devicemotion","deviceorientation", "error","focus","load","message","orientationchange", "resize","scroll","storage","click"]; const _this = this; // 用some()优化事件存在性判断,比filter()[0]更高效 const _isEventExists = window._stackedListeners.some(item => item.type === type && item.target === _this); const isHtmlElement = this instanceof HTMLElement; if (worker_events.includes(type) && !_isEventExists && isFromSourceX) { window._stackedListeners.push({ target: _this, type: type, listener: listener, params: params }); this.addEventListenerBase(type, listener, params); } }; })(self);
关键细节说明
- 调用栈解析逻辑:通过抛出临时Error获取调用栈,跳过当前工具函数和
addEventListener自身的调用行,精准定位到触发事件绑定的代码所在脚本。 - 兼容性处理:适配Chrome、Firefox等不同浏览器的调用栈格式,不管是本地
file://路径还是远程HTTPS路径,都能正确提取文件名。 - 性能优化:将原来的
filter()[0]替换为some(),判断事件是否存在时会提前终止遍历,提升代码效率。
修改后,你就能自动识别来自nameoffile.js的事件监听器,后续document.open执行后,直接遍历window._stackedListeners数组重新绑定事件即可。
内容的提问来源于stack exchange,提问作者Basilf
相关产品推荐
相关产品推荐

