40
votes

My extension is supposed to load a content script, searchTopic.js, only after the page it's injected into has already fully loaded (yes, I have set "run_at" to "document_end" in the extension manifest), but in fact it's loading before all the DOM objects have been created (the crucial ones are created via some Javascript in the page). So, the question is, how can I wait until the page's Javascript has executed? Here's my manifest:

"content_scripts": [
  {
  "run_at": "document_end",
  "matches": ["https://groups.google.com/forum/*"],
  "js": ["searchTopic.js"]
  }
],
3
You cannot predict the future. Any solution you find is specific to the situation for which you've written the script. Try to play with the window.onload event, or otherwise by a kind of polling using timers to detect when the page is in the desired state. - Rob W
You cannot know when all javascript will have been executed. It is ran once when parsed, but its actions (DOM manipulation, further script loading) might happen later, triggered by any event - and I fear those were interesting for you. - Bergi

3 Answers

64
votes

"run_at": "document_end" is the equivalent to DOMContentLoaded. That is, it fires after the static HTML is loaded, but before slow images and slow finishing javascript.

So you cannot set a content script to fire after the page's JS, just by setting the manifest alone. You must code for this in the content script itself.

For content scripts, "run_at": "document_end" will fire before the onload event (unlike the default document_idle -- which can fire at unpredictable times).

So, the first step is to wait for the load event with code like this in your content script (searchTopic.js):

window.addEventListener ("load", myMain, false);

function myMain (evt) {
    // DO YOUR STUFF HERE.
}


In the case where the script you care about takes a while to finish, you will have to poll for some condition on a case-by-case basis. For example:

window.addEventListener ("load", myMain, false);

function myMain (evt) {
    var jsInitChecktimer = setInterval (checkForJS_Finish, 111);

    function checkForJS_Finish () {
        if (    typeof SOME_GLOBAL_VAR != "undefined"
            ||  document.querySelector ("SOME_INDICATOR_NODE_css_SELECTOR")
        ) {
            clearInterval (jsInitChecktimer);
            // DO YOUR STUFF HERE.
        }
    }
}
3
votes

script elements are executed in order. The browser even stops loading DOM content if there are script nodes in the body. This is guaranteed to work, otherwise document.write() would stop working.

So you have two solutions:

  1. Use the onload event to load your code.
  2. Add a script tag as the last element of the page (last thing in the body element). This script will be executed after all other scripts have finished and after the body has been converted to a DOM tree.
-1
votes

You use window.load method.

function addLoadEvent(a) {
    var b = window.onload;
    "function" != typeof window.onload ? window.onload = a : window.onload = function () {
        b && b(),
            a()
    }
}
function init() {
    alert('init');
}
addLoadEvent(init);