【发布时间】:2021-01-14 04:04:02
【问题描述】:
基本上我希望在DIV 的内容发生变化时执行脚本。由于脚本是分开的(Chrome 扩展和网页脚本中的内容脚本),我需要一种方法来简单地观察 DOM 状态的变化。我可以设置投票,但这似乎很草率。
【问题讨论】:
-
这能回答你的问题吗? Detect changes in the DOM
标签: javascript jquery google-chrome-extension
基本上我希望在DIV 的内容发生变化时执行脚本。由于脚本是分开的(Chrome 扩展和网页脚本中的内容脚本),我需要一种方法来简单地观察 DOM 状态的变化。我可以设置投票,但这似乎很草率。
【问题讨论】:
标签: javascript jquery google-chrome-extension
许多站点使用 AJAX/XHR/fetch 来动态添加、显示、修改内容,并使用 window.history API 代替站点内导航,因此当前 URL 以编程方式更改。此类网站称为 SPA,是 Single Page Application 的缩写。
MutationObserver (docs) 从字面上检测 DOM 变化:
Performance of MutationObserver to detect nodes in entire DOM.
简单示例:
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
onUrlChange();
}
}).observe(document, {subtree: true, childList: true});
function onUrlChange() {
console.log('URL changed!', location.href);
}
事件监听器,用于通过发送 DOM 事件来表示内容更改的网站:
pjax:end on document 被许多基于 pjax 的网站使用,例如GitHub,message on window 被例如使用在 Chrome 浏览器中进行 Google 搜索,yt-navigate-finish 被 Youtube 使用,通过 setInterval 定期检查 DOM:
显然,这仅在您等待由其 id/选择器标识的特定元素出现时才有效,并且它不会让您普遍检测新的动态添加的内容,除非您发明某种指纹识别现有内容。
伪装History API:
let _pushState = History.prototype.pushState;
History.prototype.pushState = function (state, title, url) {
_pushState.call(this, state, title, url);
console.log('URL changed', url)
};
收听hashchange、popstate事件:
window.addEventListener('hashchange', e => {
console.log('URL hash changed', e);
doSomething();
});
window.addEventListener('popstate', e => {
console.log('State changed', e);
doSomething();
});
附:所有这些方法都可以在 WebExtension 的content script 中使用。这是因为我们正在查看的案例是通过 history.pushState 或 replaceState 更改 URL,因此页面本身在相同的内容脚本环境下保持不变。
【讨论】:
长期以来,DOM3 突变事件是可用的最佳解决方案,但由于性能原因,它们已被弃用。 DOM4 Mutation Observers 是已弃用的 DOM3 突变事件的替代品。它们是 currently implemented in modern browsers 和 MutationObserver (或者在旧版 Chrome 中以供应商为前缀的 WebKitMutationObserver):
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(function(mutations, observer) {
// fired when a mutation occurs
console.log(mutations, observer);
// ...
});
// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
subtree: true,
attributes: true
//...
});
此示例侦听document 及其整个子树上的 DOM 更改,并将触发元素属性的更改以及结构更改。规范草案有一个完整的有效mutation listener properties列表:
子列表
- 如果要观察目标子代的突变,则设置为
true。属性
- 如果要观察到目标属性的突变,则设置为
true。字符数据
- 如果要观察目标数据的突变,请设置为
true。子树
- 如果不仅要观察目标突变,还要观察目标后代的突变,则设置为
true。attributeOldValue
- 如果
attributes设置为 true 并且目标的属性值需要记录突变之前,则设置为true。characterDataOldValue
- 如果
characterData设置为true,则设置为true,并且需要记录突变前的目标数据。属性过滤器
- 如果不需要观察所有属性突变,则设置为属性本地名称列表(无命名空间)。
(此列表截至 2014 年 4 月;您可以查看规范是否有任何更改。)
【讨论】:
([{}]) 登录到控制台,当我单击它时显示预期的 MutationRecord。请再次检查,因为它可能是 JSFiddle 中的临时技术故障。我还没有在 IE 中测试过,因为我没有 IE 10,这是目前唯一支持突变事件的版本。
【讨论】:
除了MutationObserver API 提供的“原始”工具外,还有一些“便利”库可以处理 DOM 突变。
考虑:MutationObserver 以子树的形式表示每个 DOM 变化。因此,例如,如果您正在等待插入某个元素,则它可能位于 mutations.mutation[i].addedNodes[j] 的子元素的深处。
另一个问题是,当您自己的代码对突变做出反应时,会更改 DOM - 您通常希望将其过滤掉。
解决此类问题的一个很好的便利库是mutation-summary(免责声明:我不是作者,只是一个满意的用户),它使您能够指定对您感兴趣的内容的查询,并得到准确的结果。
文档中的基本用法示例:
var observer = new MutationSummary({
callback: updateWidgets,
queries: [{
element: '[data-widget]'
}]
});
function updateWidgets(summaries) {
var widgetSummary = summaries[0];
widgetSummary.added.forEach(buildNewWidget);
widgetSummary.removed.forEach(cleanupExistingWidget);
}
【讨论】:
另一种方法取决于您如何更改 div。 如果您使用 JQuery 通过其 html() 方法更改 div 的内容,则可以扩展该方法并在每次将 html 放入 div 时调用注册函数。
(function( $, oldHtmlMethod ){
// Override the core html method in the jQuery object.
$.fn.html = function(){
// Execute the original HTML method using the
// augmented arguments collection.
var results = oldHtmlMethod.apply( this, arguments );
com.invisibility.elements.findAndRegisterElements(this);
return results;
};
})( jQuery, jQuery.fn.html );
我们只是拦截对 html() 的调用,用 this 调用一个注册函数,在上下文中是指目标元素获取新内容,然后我们将调用传递给原始 jquery.html() 函数。请记住返回原始 html() 方法的结果,因为 JQuery 期望它用于方法链接。
有关方法覆盖和扩展的更多信息,请查看http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm,这是我编写闭包函数的地方。另请查看 JQuery 网站上的插件教程。
【讨论】: