油猴脚本监听popover渲染追加元素 移除disconnect致页面卡死如何解决
问题原因
移除this.disconnect()后页面卡顿是两个逻辑错误导致的:
- 回调中执行
appendChild修改DOM的操作,会再次触发MutationObserver回调,形成无限触发的死循环,直接占满JS主线程导致页面挂起。 - 没有做已处理元素的去重判断,同一个元素被多次扫描到时会重复执行DOM操作,产生无意义的性能开销。
修复方案
只需要两处调整即可实现持续监听所有动态弹出的popover,同时不会造成卡顿:
- 删除
this.disconnect()语句,保持监听器持续运行 - 给已经添加过Teams按钮的元素增加自定义标记,处理节点前先校验标记,跳过已处理元素,从根源避免重复操作和死循环。
修改后的完整代码如下:
// ==UserScript== // @name ms_teams_integration // @match https://example.com/* // @version 1.1 // @grant none // @run-at document-start // @require 第三方setMutationHandler库依赖地址 // ==/UserScript== var teamsIcon = ""; // 此处填写svg格式的图标内容 setMutationHandler(document, '#user-hover-email', function(nodes) { nodes.forEach(function(n) { // 跳过已经添加过按钮的元素 if (n.dataset.teamsBtnAdded) return; // 打上已处理标记 n.dataset.teamsBtnAdded = "1"; var emailLink = n.getElementsByTagName("a").item(0); var teamsLink = emailLink.cloneNode(true); teamsLink.setAttribute( "href", teamsLink.getAttribute("href") .replace("mailto:", "https://teams.microsoft.com/l/chat/0/0?users=")); teamsLink.setAttribute("target", "_blank"); teamsLink.innerHTML = teamsIcon; n.appendChild(teamsLink); }); });
可选性能优化
如果页面DOM变动非常频繁,建议放弃第三方依赖库,直接用原生MutationObserver实现,手动关闭不需要的监听项,只监听节点新增事件,完全忽略属性修改、文本修改这类无关变动,性能会更稳定,哪怕页面操作频繁也不会卡顿:
// ==UserScript== // @name ms_teams_integration // @match https://example.com/* // @version 1.1 // @grant none // @run-at document-start // ==/UserScript== var teamsIcon = ""; // 此处填写svg格式的图标内容 const TARGET_SELECTOR = '#user-hover-email'; function addTeamsButton(container) { const emailLink = container.getElementsByTagName("a").item(0); const teamsLink = emailLink.cloneNode(true); teamsLink.setAttribute( "href", teamsLink.getAttribute("href") .replace("mailto:", "https://teams.microsoft.com/l/chat/0/0?users=") ); teamsLink.setAttribute("target", "_blank"); teamsLink.innerHTML = teamsIcon; container.appendChild(teamsLink); } function processNode(node) { if (node.nodeType !== Node.ELEMENT_NODE) return; // 处理当前节点本身就是目标元素的情况 if (node.matches(TARGET_SELECTOR)) { if (node.dataset.teamsBtnAdded) return; node.dataset.teamsBtnAdded = "1"; addTeamsButton(node); } // 处理当前节点内部包含目标元素的情况 node.querySelectorAll(TARGET_SELECTOR).forEach(el => { if (el.dataset.teamsBtnAdded) return; el.dataset.teamsBtnAdded = "1"; addTeamsButton(el); }); } const observer = new MutationObserver(mutations => { for (const mutation of mutations) { mutation.addedNodes.forEach(processNode); } }); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: false, characterData: false });
内容的提问来源于stack exchange,提问作者craystern
相关产品推荐
相关产品推荐

