440

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

4

5 回答 5

532

For a long time, DOM3 mutation events were the best available solution, but they have been deprecated for performance reasons. DOM4 Mutation Observers are the replacement for deprecated DOM3 mutation events. They are currently implemented in modern browsers as MutationObserver (or as the vendor-prefixed WebKitMutationObserver in old versions of 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 document and 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:

childList

  • Set to true if mutations to target's children are to be observed.

attributes

  • Set to true if mutations to target's attributes are to be observed.

characterData

  • Set to true if mutations to target's data are to be observed.

subtree

  • Set to true if mutations to not just target, but also target's descendants are to be observed.

attributeOldValue

  • Set to true if attributes is set to true and target's attribute value before the mutation needs to be recorded.

characterDataOldValue

  • Set to true if characterData is 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.

(This list is current as of April 2014; you may check the specification for any changes.)

于 2012-07-18T16:39:10.890 回答
223

编辑

这个答案现在已被弃用。请参阅apsillers的答案。

由于这是针对 Chrome 扩展的,因此您不妨使用标准 DOM 事件 - DOMSubtreeModified. 请参阅跨浏览器对此事件的支持。Chrome 从 1.0 开始就支持它。

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

在此处查看一个工作示例。

于 2010-05-16T17:25:59.500 回答
69

Many sites use AJAX/XHR/fetch to add, show, modify content dynamically and window.history API instead of in-site navigation so current URL is changed programmatically. Such sites are called SPA, short for Single Page Application.


Usual JS methods of detecting page changes

  • MutationObserver (docs) to literally detect DOM changes.

    Info/examples:

  • Event listener for sites that signal content change by sending a DOM event:

  • 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 API:

    let _pushState = History.prototype.pushState;
    History.prototype.pushState = function (state, title, url) {
      _pushState.call(this, state, title, url);
      console.log('URL changed', url)
    };
    
  • Listening to hashchange, popstate events:

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

P.S. All these methods can be used in a WebExtension's content script. It's because the case we're looking at is where the URL was changed via history.pushState or replaceState so the page itself remained the same with the same content script environment.

于 2016-09-15T10:36:21.970 回答
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() 的调用,用 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.

于 2012-05-24T17:01:18.793 回答
7

In addition to the "raw" tools provided by MutationObserver API, there exist "convenience" libraries to work with DOM mutations.

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].

Another problem is when your own code, in reaction to mutations, changes DOM - you often want to filter it out.

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.

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);
}
于 2016-05-10T12:00:26.953 回答