How to get a content script to load AFTER a page's Javascript has executed?
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:
- Use the
onload
event to load your code. - 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.
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, 2022Comments
-
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 over 11 yearsThe 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 over 11 yearsThis 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 over 11 yearsBTW, whatever happened to answers #1 and 2?
-
Brock Adams over 11 yearsIt 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 over 11 yearsThe
window.onload
waits forasync
/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 over 11 yearsI'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 over 11 yearsIn 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 over 11 yearsYes, 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 over 11 yearsIt's the javascript, regex
test()
function, with the case-insensitive flag. -
HaveAGuess about 9 yearsSee also mutation events (untested by me yet) from this answer: stackoverflow.com/questions/17986020/…
-
Brock Adams about 9 years@HaveAGuess, mutation events are deprecated (and for good reasons).
-
HaveAGuess about 9 years@BrockAdams ok, so from that answer looks like Mutation Observers instead then right?
-
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 about 9 yearsI 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 almost 9 years+1 for the timer advice. After having tried the mutation observer approach, I can testify that it gets tricky.