Categories
google-chrome-extension javascript jquery

Is there a JavaScript / jQuery DOM change listener?

446

Essentially I want to have a script execute when the contents of a DIV change. 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.

1

539

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

11

  • 3

    @AshrafBashir I see the sample working fine in Firefox 19.0.2: I see ([{}]) logged to the console, which shows the expected MutationRecord when I click on it. Please check again, as it might have been a temporary technical failure in JSFiddle. I have not tested it in IE yet, since i don’t have IE 10, which is currently the only version to support mutation events.

    – apsillers

    Mar 25, 2013 at 15:20


  • 1

    I just posted an answer that works in IE10+ and mostly anything else.

    – naugtur

    Mar 6, 2014 at 22:22

  • 1

    The specification doesn’t seem to have a green box that lists the mutation observer options any longer. It does list the options in section 5.3.1 and describes them just a little further below that.

    Apr 8, 2014 at 13:01

  • 2

    @LS Thanks, I’ve updated the link, removed the bit about the green box, and edited the entire list into my answer (just in case of future link rot).

    – apsillers

    Apr 8, 2014 at 14:27

  • 4

    Here’s a browser compatibility table from Can I Use.

    – bdesham

    Jul 8, 2014 at 14:47

223

Edit

This answer is now deprecated. See the answer by apsillers.

Since this is for a Chrome extension, you might as well use the standard DOM event – DOMSubtreeModified. See the support for this event across browsers. It has been supported in Chrome since 1.0.

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

See a working example here.

8

77

+50

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.

0