IT笔记bug一个困扰我半年的bug,cannot read properties of null (reading 'addeventlistener'),真凶竟是篡改猴!
何平安没错如果你的Vue出现了cannot read properties of null (reading
‘addeventlistener’),而且下面的索引为userscript.html的212行,内容为:
1 2 3 4
| function setBtnClick(){ document.querySelector("#rwl-setbtn").addEventListener("click",setMenu); }
|
一开始我以为是全局配置错了啥的,网上搜出来说还要挂载dom前停止addEventLisener事件啥的,甚至我把addeventlistener的所有代码都删了,试了半天都不行。
罪魁祸首:

修改方法
那么恭喜你,你写代码的时候不能用篡改猴了,或者不闲麻烦可以使用下面的修改方法:
在 Tampermonkey(篡改猴)测试版中遇到 Cannot read properties of null (reading 'addEventListener')
错误,通常是因为用户脚本尝试访问尚未加载或动态生成的 DOM 元素。以下是针对 Tampermonkey 环境的详细解决方案:
1. 确保脚本在 DOM 加载完成后执行
Tampermonkey 默认在 document-end
阶段执行脚本,但某些动态内容可能需要更晚的执行时机。使用 @run-at
元数据或 DOMContentLoaded
事件确保元素存在。
方法 1:调整脚本执行阶段
在脚本的元数据(==UserScript==
部分)中指定 @run-at document-idle
,确保脚本在页面完全空闲时执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
(function() { 'use strict'; const btn = document.querySelector("#rwl-setbtn"); if (btn) { btn.addEventListener("click", setMenu); } else { console.error("未找到按钮 #rwl-setbtn"); } })();
|
方法 2:手动监听 DOMContentLoaded
即使使用 @run-at
,某些动态页面仍需进一步等待:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
document.addEventListener("DOMContentLoaded", function() { const btn = document.querySelector("#rwl-setbtn"); if (btn) { btn.addEventListener("click", setMenu); } else { console.warn("初次未找到按钮,尝试动态监听..."); observeDynamicButton(); } });
function observeDynamicButton() { const observer = new MutationObserver(() => { const btn = document.querySelector("#rwl-setbtn"); if (btn) { btn.addEventListener("click", setMenu); observer.disconnect(); console.log("动态按钮已绑定事件"); } }); observer.observe(document.body, { childList: true, subtree: true }); }
function setMenu() { }
|
2. 处理跨域或 iframe 中的元素
如果目标元素位于 iframe
内,需先访问 iframe
的 contentDocument
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
document.addEventListener("DOMContentLoaded", function() { const iframe = document.querySelector("iframe"); if (iframe) { iframe.addEventListener("load", function() { const btn = iframe.contentDocument.querySelector("#rwl-setbtn"); if (btn) { btn.addEventListener("click", setMenu); } }); } });
|
3. 使用 Tampermonkey 的 unsafeWindow
访问页面上下文
Tampermonkey 脚本默认运行在隔离的沙盒环境,直接使用 document
可能无法访问页面 DOM。需通过 unsafeWindow
访问实际页面对象:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
const uw = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
document.addEventListener("DOMContentLoaded", function() { const btn = uw.document.querySelector("#rwl-setbtn"); if (btn) { btn.addEventListener("click", setMenu); } });
|
4. 调试与日志输出
添加日志以确认元素是否存在及脚本执行状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
console.log("Tampermonkey 脚本已加载");
document.addEventListener("DOMContentLoaded", function() { console.log("DOMContentLoaded 触发"); const btn = document.querySelector("#rwl-setbtn"); console.log("查找到的按钮:", btn); if (btn) { btn.addEventListener("click", function() { console.log("按钮点击事件触发"); setMenu(); }); } });
|
5. 验证元素选择器
确保 #rwl-setbtn
选择器正确:
- 在目标页面上按
F12
打开开发者工具。
- 使用
Ctrl+F
在 Elements 面板中搜索 #rwl-setbtn
。
- 确认元素存在且 ID 无误。如果元素是动态生成的,检查其父级容器是否已渲染。
6. 处理 Shadow DOM
如果元素位于 Shadow DOM 内部,需穿透访问:
1 2 3 4 5 6 7 8
| const host = document.querySelector("#shadow-host"); if (host && host.shadowRoot) { const btn = host.shadowRoot.querySelector("#rwl-setbtn"); if (btn) { btn.addEventListener("click", setMenu); } }
|
总结
- 执行时机:通过
@run-at document-idle
或 DOMContentLoaded
确保脚本在 DOM 就绪后运行。
- 动态元素:使用
MutationObserver
监听 DOM 变化。
- 跨域/iframe:通过
iframe.contentDocument
或 unsafeWindow
访问目标元素。
- 调试:添加
console.log
验证元素是否存在及脚本执行流程。
- 选择器验证:确保元素 ID 正确且位于当前作用域。
如果问题仍未解决,请提供目标页面的 URL 或 HTML 结构,以便进一步分析。