是否有 JavaScript / jQuery DOM 更改侦听器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2844565/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a JavaScript / jQuery DOM change listener?
提问by Fletcher Moore
Essentially I want to have a script execute when the contents of a DIVchange. Since the scripts are separate (content script in the Chrome extension & webpage script), I need a way simply observe changes in DOM state. I could set up polling but that seems sloppy.
本质上,我希望在DIV更改内容时执行脚本。由于脚本是独立的(Chrome 扩展程序和网页脚本中的内容脚本),我需要一种方法来简单地观察 DOM 状态的变化。我可以设置投票,但这似乎很草率。
回答by apsillers
For a long time, DOM3 mutation events were the best available solution, but they have been deprecated for performance reasons. DOM4 Mutation Observersare the replacement for deprecated DOM3 mutation events. They are currently implemented in modern browsersas MutationObserver(or as the vendor-prefixed WebKitMutationObserverin old versions of Chrome):
长期以来,DOM3 突变事件是最好的可用解决方案,但由于性能原因,它们已被弃用。DOM4 Mutation Observers是已弃用的 DOM3 突变事件的替代品。它们目前在现代浏览器中实现为MutationObserver(或作为WebKitMutationObserver旧版 Chrome 中的供应商前缀):
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
//...
});
This example listens for DOM changes on documentand its entire subtree, and it will fire on changes to element attributes as well as structural changes. The draft spec has a full list of valid mutation listener properties:
此示例侦听 DOMdocument及其整个子树的更改,并将触发元素属性的更改以及结构的更改。规范草案有一个完整的有效突变监听器属性列表:
childList
- Set to
trueif mutations to target's children are to be observed.attributes
- Set to
trueif mutations to target's attributes are to be observed.characterData
- Set to
trueif mutations to target's data are to be observed.subtree
- Set to
trueif mutations to not just target, but also target's descendants are to be observed.attributeOldValue
- Set to
trueifattributesis set to true and target's attribute value before the mutation needs to be recorded.characterDataOldValue
- Set to
trueifcharacterDatais set to true and target's data before the mutation needs to be recorded.attributeFilter
- Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed.
子列表
true如果要观察目标孩子的突变,则设置为。属性
true如果要观察目标属性的突变,则设置为。字符数据
true如果要观察目标数据的突变,则设置为。子树
- 设置为
trueif 突变不仅要观察目标,还要观察目标的后代。属性旧值
- 设置为
trueifattributes设置为true,需要记录突变前target 的属性值。字符数据旧值
- 设置为
trueifcharacterData设置为 true 并且需要记录突变之前目标的数据。属性过滤器
- 如果不需要观察所有属性突变,则设置为属性本地名称列表(不带命名空间)。
(This list is current as of April 2014; you may check the specification for any changes.)
(此列表截至 2014 年 4 月是最新的;您可以查看规范以了解任何更改。)
回答by Anurag
Edit
编辑
This answer is now deprecated. See the answer by apsillers.
此答案现已弃用。请参阅apsillers的答案。
Since this is for a Chrome extension, you might as well use the standard DOM event - DOMSubtreeModified. See the support for this eventacross browsers. It has been supported in Chrome since 1.0.
由于这是针对 Chrome 扩展程序,您不妨使用标准 DOM 事件 - DOMSubtreeModified。查看跨浏览器对此事件的支持。Chrome 从 1.0 开始就支持它。
$("#someDiv").bind("DOMSubtreeModified", function() {
alert("tree changed");
});
See a working example here.
请参阅此处的工作示例。
回答by wOxxOm
Many sites use AJAX to add/show/change content dynamically. Sometimes it's used instead of in-site navigation, so current URL is changed programmatically and content scripts aren't automatically executed by browser in this case since the page isn't fetched from remote server entirely.
许多站点使用 AJAX 动态添加/显示/更改内容。有时它被用来代替站点内导航,因此当前 URL 以编程方式更改,并且在这种情况下浏览器不会自动执行内容脚本,因为页面不是完全从远程服务器获取的。
Usual JS methods of detecting page changes available in a content script.
检测内容脚本中可用的页面更改的常用 JS 方法。
MutationObserver(docs) to literally detect DOM changes:
Event listenerfor sites that signal content change by sending a DOM event:
pjax:endondocumentused by many pjax-based sites e.g. GitHub,
see How to run jQuery before and after a pjax load?messageonwindowused by e.g. Google search in Chrome browser,
see Chrome extension detect Google search refreshspfdoneondocumentused by Youtube,
see How to detect page navigation on Youtube and modify HTML before page is rendered?
Periodic checking of DOM via setInterval:
Obviously this will work only in cases when you wait for a specific element identified by its id/selector to appear, and it won't let you universally detect new dynamically added content unless you invent some kind of fingerprinting the existing contents.Cloaking History APIinside an injected DOM script:
document.head.appendChild(document.createElement('script')).text = '(' + function() { // injected DOM script is not a content script anymore, // it can modify objects and functions of the page var _pushState = history.pushState; history.pushState = function(state, title, url) { _pushState.call(this, state, title, url); window.dispatchEvent(new CustomEvent('state-changed', {detail: state})); }; // repeat the above for replaceState too } + ')(); this.remove();'; // remove the DOM script element // And here content script listens to our DOM script custom events window.addEventListener('state-changed', function(e) { console.log('History state changed', e.detail, location.hash); doSomething(); });Listening to hashchange, popstateevents:
window.addEventListener('hashchange', function(e) { console.log('URL hash changed', e); doSomething(); }); window.addEventListener('popstate', function(e) { console.log('State changed', e); doSomething(); });
MutationObserver( docs) 从字面上检测 DOM 更改:
通过发送 DOM 事件来指示内容更改的站点的事件侦听器:
pjax:end在document许多基于 pjax 的站点(例如 GitHub)上使用,
请参阅如何在 pjax 加载之前和之后运行 jQuery?message上window使用的Chrome浏览器如谷歌搜索,
看到Chrome扩展检测谷歌搜索刷新spfdone上document使用的Youtube,
看看如何检测在Youtube和修改HTML页面导航页面呈现前?
通过 setInterval 定期检查 DOM:
显然,这仅在您等待由其 id/选择器标识的特定元素出现的情况下才有效,并且除非您发明某种指纹识别,否则它不会让您普遍检测新的动态添加的内容现有的内容。document.head.appendChild(document.createElement('script')).text = '(' + function() { // injected DOM script is not a content script anymore, // it can modify objects and functions of the page var _pushState = history.pushState; history.pushState = function(state, title, url) { _pushState.call(this, state, title, url); window.dispatchEvent(new CustomEvent('state-changed', {detail: state})); }; // repeat the above for replaceState too } + ')(); this.remove();'; // remove the DOM script element // And here content script listens to our DOM script custom events window.addEventListener('state-changed', function(e) { console.log('History state changed', e.detail, location.hash); doSomething(); });监听hashchange, popstate事件:
window.addEventListener('hashchange', function(e) { console.log('URL hash changed', e); doSomething(); }); window.addEventListener('popstate', function(e) { console.log('State changed', e); doSomething(); });
Extensions-specific: detect URL changes in a background/ event page.
特定于扩展程序:检测背景/事件页面中的URL 更改。
There are advanced API to work with navigation: webNavigation, webRequest, but we'll use simple chrome.tabs.onUpdatedevent listener that sends a messageto the content script:
有用于导航的高级 API:webNavigation、webRequest,但我们将使用简单的chrome.tabs.onUpdated事件侦听器向内容脚本发送消息:
manifest.json:
declare background/event page
declare content script
add"tabs"permission.background.js
var rxLookfor = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/; chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (rxLookfor.test(changeInfo.url)) { chrome.tabs.sendMessage(tabId, 'url-update'); } });content.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { if (msg === 'url-update') { doSomething(); } });
背景.js
var rxLookfor = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/; chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (rxLookfor.test(changeInfo.url)) { chrome.tabs.sendMessage(tabId, 'url-update'); } });内容.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { if (msg === 'url-update') { doSomething(); } });
回答by Zac Imboden
Another approach depending on how you are changing the div. If you are using JQuery to change a div's contents with its html() method, you can extend that method and call a registration function each time you put html into a div.
另一种方法取决于您如何更改 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 );
We just intercept the calls to html(), call a registration function with this, which in the context refers to the target element getting new content, then we pass on the call to the original jquery.html() function. Remember to return the results of the original html() method, because JQuery expects it for method chaining.
我们只是拦截对 html() 的调用,用 this 调用一个注册函数,它在上下文中指的是目标元素获取新内容,然后我们将调用传递给原始的 jquery.html() 函数。请记住返回原始 html() 方法的结果,因为 JQuery 期望它用于方法链接。
For more info on method overriding and extension, check out http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm, which is where I cribbed the closure function. Also check out the plugins tutorial at JQuery's site.
有关方法覆盖和扩展的更多信息,请查看http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm,这是我写了闭包函数。还可以查看 JQuery 站点上的插件教程。
回答by Xan
In addition to the "raw" tools provided by MutationObserverAPI, there exist "convenience" libraries to work with DOM mutations.
除了MutationObserverAPI提供的“原始”工具之外,还存在处理 DOM 突变的“便利”库。
Consider: MutationObserver represents each DOM change in terms of subtrees. So if you're, for instance, waiting for a certain element to be inserted, it may be deep inside the children of mutations.mutation[i].addedNodes[j].
考虑: MutationObserver 代表子树方面的每个 DOM 更改。因此,例如,如果您正在等待插入某个元素,它可能位于mutations.mutation[i].addedNodes[j].
Another problem is when your own code, in reaction to mutations, changes DOM - you often want to filter it out.
另一个问题是,当您自己的代码对突变做出反应时,更改了 DOM - 您通常希望将其过滤掉。
A good convenience library that solves such problems is mutation-summary(disclaimer: I'm not the author, just a satisfied user), which enables you to specify queries of what you're interested in, and get exactly that.
一个很好的解决此类问题的便利库是mutation-summary(免责声明:我不是作者,只是一个满意的用户),它使您能够指定您感兴趣的查询,并准确地得到它。
Basic usage example from the docs:
文档中的基本用法示例:
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);
}

