github.com中使用MutationObserver监听节点变更偶发失效如何解决
问题原因
- 你初始绑定监听器时获取的
<title>是代码执行瞬间存在的DOM节点,GitHub(含Gist站点)基于Turbo实现单页无刷新跳转,路由切换(比如点击Revisions按钮触发页面状态切换)时会直接替换整个<title>元素,你绑定监听器的旧title节点会被移出DOM树成为游离节点,后续新title的变化自然无法触发绑定在旧节点上的回调。 - 初始监听配置仅开启了
childList: true,覆盖场景不全:部分场景下修改标题是直接修改title下已有文本节点的内容,触发的是文本节点的characterData变更,不会触发childList回调,本身就存在漏判可能。
解决方案
直接监听持久存在的document.head节点,同时覆盖节点替换、子节点增减、文本内容变更三类场景,通过对比document.title的实际值判断标题是否真的变更,代码如下:
const ObserverCtor = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; let lastTitle = document.title; new ObserverCtor(() => { const currentTitle = document.title; if (currentTitle !== lastTitle) { lastTitle = currentTitle; alert('Monitor to page title change'); } }).observe(document.head, { childList: true, subtree: true, characterData: true });
该方案的优势:
document.head是页面加载完成后就持久存在、不会被单页路由逻辑替换的节点,不会出现监听目标丢失的问题- 三个监听配置组合覆盖了title被整体替换、title内新增/删除文本节点、已有文本节点内容修改所有会导致页面标题变化的场景
- 通过对比
document.title实际值去重,避免无关DOM变更触发无意义的回调
如果需要更低的性能开销,也可以在监听到head的childList变化时检测title节点是否被替换,动态断开旧节点的监听、重新给新title节点绑定回调,但实现逻辑更繁琐,实际场景下上述方案的性能影响完全可以忽略。
内容的提问来源于stack exchange,提问作者沙漠之子
相关产品推荐
相关产品推荐

