How to get a content script to load AFTER a page's Javascript has executed?

41,078

Solution 1

"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.
        }
    }
}

Solution 2

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.
Share:
41,078
FractalBob
Author by

FractalBob

I'm an inventor and software developer (Android and Java, mostly), currently running my company, ChiaraMail. We make fun and cool products, mostly for Android devices.

Updated on July 10, 2022

Comments

  • FractalBob
    FractalBob almost 2 years

    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"]
      }
    ],
    
  • Brock Adams
    Brock Adams over 11 years
    The script elements can be deferred or asynch. Or the script may be loaded dynamically. Or the script can be loaded, but the contained/linked code takes a bit to finish initializing.
  • FractalBob
    FractalBob over 11 years
    This is the problem: the page I'm looking at (groups.google.com/forum/?fromgroups=#!searchin/…) doesn't have any predictable condition to poll for; at least, none that I've been able to find.
  • FractalBob
    FractalBob over 11 years
    BTW, whatever happened to answers #1 and 2?
  • Brock Adams
    Brock Adams over 11 years
    It does have such a condition, or else you couldn't say that searchTopic.js had loaded too early. What, exactly are you trying to do, and what error messages do you get on the console, if any. ... What do you mean "answers #1 and 2"? One of the answers to this question was deleted by its poster. It did not solve your problem.
  • apsillers
    apsillers over 11 years
    The window.onload waits for async/defer scripts to load and execute, so that will work better than adding a script at the bottom of the page. (See "The download of an external script must delay the load event." on the W3C's HTML API.) The general problem of waiting for asynchronous script behaviors (Ajax, setTimeout, events) remains, but that is not solvable in a general way.
  • FractalBob
    FractalBob over 11 years
    I'm trying to determine if a topic search in a Google group yielded any results; if so, do one thing and if not, another. To do this, I look for the classname "gwt-Label". It seems that, once the page is fully loaded, when there are no search hits, document.getElementsByClassName("gwt-Label") has length 3; otherwise, it has length 2. If the page hasn't fully loaded, the above don't hold.
  • Brock Adams
    Brock Adams over 11 years
    In that case, you can use a test like: var keyNode = document.querySelector ("div[role='list'] div.gwt-Label"); if (keyNode && /No results found/i.test (keyNode.textContent) ) {.... which only passes on searches with no results. Or just continually wait for search results and act on them as they appear. Like with this utility.
  • FractalBob
    FractalBob over 11 years
    Yes, that should work as long as the user is in the EN locale (which is why I rejected that approach originally). But what is i.test?
  • Brock Adams
    Brock Adams over 11 years
    It's the javascript, regex test() function, with the case-insensitive flag.
  • HaveAGuess
    HaveAGuess about 9 years
    See also mutation events (untested by me yet) from this answer: stackoverflow.com/questions/17986020/…
  • Brock Adams
    Brock Adams about 9 years
    @HaveAGuess, mutation events are deprecated (and for good reasons).
  • HaveAGuess
    HaveAGuess about 9 years
    @BrockAdams ok, so from that answer looks like Mutation Observers instead then right?
  • Brock Adams
    Brock Adams about 9 years
    @HaveAGuess, yes, they would work (usually) but they often get tricky in practice. A much simpler timer works every time, is more robust and more portable.
  • HaveAGuess
    HaveAGuess about 9 years
    I guess I was just wanting to get moving as soon as the element Im waiting for arrived and didnt want to spin wait by some arbitrary amount as it might slow down the experience - especially for large number of elements
  • Mitch
    Mitch almost 9 years
    +1 for the timer advice. After having tried the mutation observer approach, I can testify that it gets tricky.