Using mutationObserver doesn't seem to keep track of a changing table in the DOM

roostaamir picture roostaamir · Nov 25, 2014 · Viewed 7.3k times · Source

I am trying to write a simple chrome extension that changes the values of a table in the DOM.The important thing here is that the webpage is using ajax commands to build and change the contents of this table.(First, there is nothing in the page.Then you click on a link, and with an ajax request(no page reloads) a table will be created and after that by clicking other links, this table will be modified).

Here's the code I am using to listen to the table's changes :

var target = document.querySelector('#my-table');

var observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        alert('The table has appeared');
    });
});

var config = {
    attributes: true,
    childList: true,
    characterData: true,
    subtree: true
};

observer.observe(target, config);

observer.disconnect();

The problem is that nothing will happens when I press a link in the page that makes the table change.And when I was inspecting chrome's console to debug my work, I saw that it gave me this error as soon as the page loads :

Uncaught NotFoundError: Failed to execute 'observe' on 'MutationObserver': The provided node was null

I am really lost and don't know what to do.Does anyone have any suggestions? ps:Here's my extension's manifest file:

"manifest_version": 2,

  "name": "MyExtension",
  "description": "Testing the Content Script api",
  "version": "1.0",

  "content_scripts": [
    {
      "matches": ["www.myWebsite.com"],
      "js": ["script.js"]
    }
  ],

  "browser_action": {
    "default_icon": "icon.png"
  }
}

Answer

Julian picture Julian · Nov 26, 2014

You need to re-evaluate the selection after the mutation, inspecting each added element in turn.

  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      mutation.addedNodes.forEach(function(node) {
        if (node.id == 'my-table') { // or node.getElementsByClassName(..).length or whatever
          console.log('table has appeared.');
        });
      });
    });
  });
  observer.observe(document, {
    childList: true,
    subtree: true,
    attributes: false,
    characterData: false,
  });

Less efficiently, but shorter code, you can re-select the entire document after each mutation:

  var observer = new MutationObserver(function() {
    if (document.getElementById('my-table')) {
      console.log('table has appeared.');
    }
  });
  observer.observe(document, {
    childList: true,
    subtree: true,
    attributes: false,
    characterData: false,
  });

If you are using a simple ID selector, it might be efficient since selecting by id is highly optimized.