是否有JavaScript / jQuery DOM更改侦听器?


402

本质上,我希望DIV更改内容时执行脚本。由于脚本是分开的(Chrome扩展程序和网页脚本中的内容脚本),因此我需要一种方法来简单地观察DOM状态的变化。我可以设置轮询,但这似乎草率。

Answers:


488

长期以来,DOM3突变事件是最好的可用解决方案,但由于性能原因,已不推荐使用它们。DOM4突变观察者代替了不推荐使用的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
  //...
});

此示例侦听DOM document及其整个子树上的更改,并将在元素属性更改和结构更改时触发。规范草案包含有效的变异侦听器属性的完整列表:

childList

  • 设置为true是否要观察目标儿童的突变。

属性

  • 设置为true是否要观察到目标属性的突变。

characterData

  • 设置为true是否要观察到目标数据的变异。

子树

  • 设置为true是否不仅要观察目标,还要观察目标的后代的突变。

attributeOldValue

  • 设置为trueif时attributes将其设置为true,并且需要记录突变之前的目标属性值。

characterDataOldValue

  • 设置为trueif如果characterData设置为true,则需要记录突变之前的目标数据。

attributeFilter

  • 如果不需要观察所有属性突变,则设置为属性本地名称(无名称空间)的列表。

(此列表是截至2014年4月的最新信息;您可以检查规格中是否有任何更改。)


3
@AshrafBashir我看到该示例在Firefox 19.0.2中可以正常工作:我看到已([{}])登录到控制台,MutationRecord单击该控制台将显示预期的效果。请再次检查,因为这可能是JSFiddle中的暂时性技术故障。我尚未在IE中进行测试,因为我没有IE 10,它是当前唯一支持突变事件的版本。
apsillers 2013年

1
我刚刚发布了一个适用于IE10 +以及大多数其他应用的答案。
naugtur 2014年

1
该规范似乎没有再列出突变观察者选项的绿色框。它确实列出了第5.3.1节中的选项,并在其下方进行了一些描述。
2014年

2
@LS谢谢,我已经更新了链接,删除了绿色框的部分,然后将整个列表编辑到我的答案中(以防将来链接腐烂)。
apsillers 2014年


211

编辑

现在不赞成使用此答案。见a虫者的答案

由于这是针对Chrome扩展程序的,因此您最好使用标准DOM事件- DOMSubtreeModified。请参阅跨浏览器对此事件的支持。从1.0开始,Chrome已支持该功能。

$("#someDiv").bind("DOMSubtreeModified", function() {
    alert("tree changed");
});

在这里查看工作示例。


我注意到,即使选择了某些选择器,也可以触发此事件。我目前正在调查。
Peder Rice

9
w3.org/TR/DOM-Level-3-Events/#event-type-DOMSubtreeModified说,此事件已被弃用,我们将使用什么呢?
马斯洛2012年


4
github.com/joelpurra/jquery-mutation-summary基本上为jquery用户解决了。
Capi Etheriel

1
如果您要建立Chrome扩充功能,仍然可以使用。无价。
concept47 2012年

49

许多站点使用AJAX动态添加/显示/更改内容。有时,它代替现场导航而使用,因此在这种情况下,当前的URL会以编程方式更改,并且内容脚本不会自动由浏览器执行,因为无法完全从远程服务器获取页面。


内容脚本中可用的通常JS方法来检测页面更改。

  • MutationObserverdocs)从字面上检测DOM更改:

  • 通过发送DOM事件来发出内容更改信号的站点的事件侦听器:

  • 通过setInterval进行DOM的定期检查
    显然,这仅在以下情况下有效:当您等待由其ID /选择器标识的特定元素出现时,除非您发明了某种指纹识别方法,否则它不会让您普遍检测到动态添加的新内容。现有内容。

  • 注入的DOM脚本内隐藏历史记录API

    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();
    });
  • hashchangepopstate事件:

    window.addEventListener('hashchange', function(e) {
        console.log('URL hash changed', e);
        doSomething();
    });
    window.addEventListener('popstate', function(e) {
        console.log('State changed', e);
        doSomething();
    });



特定于扩展名:检测 后台 / 事件页面中的

有一些用于导航的高级API:webNavigationwebRequest,但是我们将使用简单的chrome.tabs.onUpdated事件侦听器,该消息将消息发送到内容脚本:

  • manifest.json:
    声明背景/事件页面
    声明内容脚本
    添加"tabs" 权限

  • 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();
        }
    });

29

另一种方法取决于您如何更改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()的调用,并以此调用一个注册函数,在上下文中它是指目标元素获取新内容,然后将调用传递给原始的jquery.html()函数。记住要返回原始html()方法的结果,因为JQuery希望将其用于方法链接。

有关方法重写和扩展的更多信息,请访问http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm,该文件位于我将闭包函数命名为crisp。还可以在JQuery的站点上查看插件教程。


7

除了MutationObserverAPI提供的“原始”工具外,还存在用于处理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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.