Search code examples
javascriptfirefoxfirefox-addonfirefox-addon-sdkmutation-observers

Iterating through and modifying DOM elements added via Ajax without jQuery


I'm working on a small Firefox Addon that uses PageMod to process image elements loaded in a web page. I've seen examples on the web that used jQuery but since I'm adding a content script to every webpage, adding jQuery might cause irreparable damage.

Since Firefox supports MutationObserver I have tried to do the following thanks to AndrewVermie :

content.js

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {

    if(mutation.tagName == "IMG")
    {
        //I'm not sure this object can be treated as a DOM Element (?)
        mutation.src = "http://s6.tinypic.com/15npphk_th.jpg";
    }

    for (var i = 0; i < mutation.addedNodes.length; ++i) {
          var node = mutation.addedNodes[i],
              matches = [].slice.call(node.querySelectorAll('img'));

          for (var j = 0; j < matches.length; ++j) {
              var img = matches[j];
              img.src = "http://s6.tinypic.com/15npphk_th.jpg";
          }
      }
   });
});

var config = { attributes: true, childList: true, subtree: true, characterData: true};
observer.observe(window.document, config);


var elements = document.getElementsByTagName("img");
var elementsLength = elements.length;
for (var i = 0; i < elementsLength; i++) 
{
    elements[i].src = "http://s6.tinypic.com/15npphk_th.jpg";
}

main.js

pageMod.PageMod({
    include: "*",
    contentScriptWhen: 'ready',
    contentScriptFile: [self.data.url("content.js")],
    onAttach: //...
    }
});

What happens :

  1. Go to http://imgur.com/
  2. Start scrolling until images are loaded dynamically
  3. Notice that they are not changing

I console.log() every .tagName and only see p and #text when I hover over images.

What I need

Iterate through each element loaded dynamically within a webpage in which the script is injected and be able to modify the element's attributes.

If it can be done with pure JavaScript and with or without MutationObserver that'll be really swell.

Otherwise, if I can find out just that some elements have been loaded to reiterate through ALL the elements of the page again, that'll be OK but not swell.

I hope I've cleared things up a bit.

Any help would be greatly appreciated.


Solution

  • Your mistake here is to think that all nodes actually have a querySelectorAll method. E.g. text nodes do not! So you called a non-existent method, which threw an exception and ended your processing early (as the first mutation node on imgur.com is actually a text node).

    Here is what I came up with, what seems to do what you're after, ecma-6-ified a bit :p. My content script:

    var observer = new MutationObserver(function(mutations) {
      for (var m of mutations) {
        for (var node of m.addedNodes) {
          if (!node || !node.querySelectorAll) {
            // Not all nodes support querySelectorAll, e.g. text nodes.
            continue;
          }
          for (var img of node.querySelectorAll("img")) {
            img.src = "http://s6.tinypic.com/15npphk_th.jpg";
          }
        }
      }
    });
    
    var config = { attributes: true, childList: true, subtree: true, characterData: true};
    observer.observe(window.document, config);
    
    var elements = document.getElementsByTagName("img");
    var elementsLength = elements.length;
    for (var i = 0; i < elementsLength; i++) {
        elements[i].src = "http://s6.tinypic.com/15npphk_th.jpg";
    }