thefoyer
thefoyer

Reputation: 314

Loading script after page fully loaded

I am building a firefox addon that loads javascript at every page load. I'm using progress listener function I found on this page: https://developer.mozilla.org/en/Code_snippets/Progress_Listeners

My problem is that the code seems to execute to early before the page is fully loaded which causes my script to not run. Here is my code.

var PageLoad = {
    browser: null,
    domain:  null,
    oldURL:  null,

    init: function() {
            gBrowser.addProgressListener(urlBarListener,Components.interfaces.nsIWebProgress.NOTIFY_LOCATION);
    },

    uninit: function() {
            gBrowser.removeProgressListener(urlBarListener);
    },

    processNewURL: function(aURI) {
            //if (aURI.spec == this.oldURL)
            //return;

        MyObject.function();

            this.oldURL = aURI.spec;
    }
};

var urlBarListener = {
    locChange: false,

    QueryInterface: function(aIID) {
            if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
               aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
               aIID.equals(Components.interfaces.nsISupports))
            return this;
            throw Components.results.NS_NOINTERFACE;
    },

    onLocationChange: function(aProgress, aRequest, aURI) {
            PageLoad.processNewURL(aURI);
    },

    onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) {},
    onProgressChange: function(a, b, c, d, e, f) {},
    onStatusChange: function(a, b, c, d) {},
    onSecurityChange: function(a, b, c) {}
};

window.addEventListener("load",
    function() {
            PageLoad.init()
    }, false);

var MyObject = {
    function : function() {
            var script = PageLoad.browser.createElement('script');
            script.src = 'url_to_script.js';
            PageLoad.browser.getElementsByTagName('head')[0].appendChild(script);
    }
};

With this code I get this error message on the console:

PageLoad.browser.getElementByTagName("head")[0] is undefined

If I add a timeout then the script does work intermittently but if the page loads slow I get the same error, here's what works sometimes setTimeout(MyObject.function, 1000);

I need a more reliable way of making sure it's executing the script after the page is loaded.

Unrelated, and it doesn't seem to cause any problems but I also see this error message:

gBrowser.addProgressListener was called with a second argument, which is not supported. See bug 608628.

Upvotes: 1

Views: 3265

Answers (4)

Idan Gozlan
Idan Gozlan

Reputation: 3203

Actually its a great question. You should use event listener, but carefully, because if you trigger for every page load its can trigger you more than one time (in the worst case about dozens of times). So how you can do that?

window.addEventListener("load", function load(event){
    window.removeEventListener("load", load, false); //remove listener, no longer needed
    myExtension.init(); 
},false);

var myExtension = {
    init: function() {
    var appcontent = document.getElementById("appcontent");   // browser
    if(appcontent){
      appcontent.addEventListener("DOMContentLoaded", myExtension.onPageLoad, true);
    }

  },

  onPageLoad: function(aEvent) {
    var doc = aEvent.originalTarget; // doc is document that triggered the event
    var win = doc.defaultView; // win is the window for the doc

    if (doc.nodeName != "#document") return;
    if (win != win.top) return;
    if (win.frameElement) return;

      alert("the main page has been loaded");

  },
};

get notice that we check for the type of the trigger every pageload triggering to prevent multi load.

Upvotes: 1

thefoyer
thefoyer

Reputation: 314

The answers that were provided were acceptable but I found yet another solution that works perfectly.

var PageLoad = {
init: function() {
    if(gBrowser) gBrowser.addEventListener("DOMContentLoaded", this.onPageLoad, false);
  },
  onPageLoad: function(aEvent) {
    var doc = aEvent.originalTarget; // doc is document that triggered the event
    var win = doc.defaultView; // win is the window for the doc

    if (doc.nodeName != "#document") return;
    if (win != win.top) return;
    if (win.frameElement) return;

    MyAddon.function();
  }
}

window.addEventListener("load", function load(event){
  window.removeEventListener("load", load, false); //remove listener, no longer needed
  PageLoad.init();  
},false);

Upvotes: -1

KAdot
KAdot

Reputation: 2077

If you want to load javascript at every page load - the best way is subscribing to DOMContentLoaded event:

var MyObject  = {
    processDOMContentLoaded: function(doc) {
        var script = doc.createElement('script');
        script.src = 'url_to_script.js';
        script.type = 'text/javascript';
        doc.getElementsByTagName('head')[0].appendChild(script);
    }
};

window.addEventListener('load', function() {
    var appcontent = document.getElementById('appcontent');
    if(appcontent != null) {
        appcontent.addEventListener('DOMContentLoaded', function(event) {
            var doc = event.originalTarget;
            if(doc instanceof HTMLDocument) {
                MyObject.processDOMContentLoaded(doc);
            }
        }, true);
    }
}, false);

Have not tested, but should work.

Upvotes: 2

Wladimir Palant
Wladimir Palant

Reputation: 57651

You are using onLocationChange method - but if you look at how the browser behaves, the location in the address bar changes as soon as a connection with the server is established. You should look at state changes instead:

onStateChange: function(aWebProgress, aRequest, aFlag, aStatus)
{
  if ((aFlag & Components.interfaces.nsIWebProgressListener.STATE_STOP) &&
      (aFlag & Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW))
  {
    // A window finished loading
    PageLoad.windowLoaded(aWebProgress.DOMWindow);
  }
},

Note that the window that finished loading is explicitly passed to PageLoad.windowLoaded() - you will be receiving notifications from different tabs and you cannot assume that the notification comes from the foreground tab.

As to the warning you are getting, just leave out the second parameter in the call to gBrowser.addProgressListener():

gBrowser.addProgressListener(urlBarListener);

tabbrowser.addProgressListener() only accepts one parameter, unlike nsIWebProgress.addProgressListener() which has a second parameter.

Upvotes: 1

Related Questions