1

As the title says, I want to wait until the html is parsed, and all stylesheets are loaded, because I need to use currentsyle. However, I do not want to wait for images. So I cannot use load event, as that waits for images. And I can't use the DOMContentLoaded event, as that doesn't wait for stylesheets. I also cannot use document.body.appendChild to add a script tag at the end of the document because this script is being run under the assumption that javascript on the page is disabled.

Is there seriously no event to wait on styles?

user234683
  • 323
  • 3
  • 8

1 Answers1

0

There are three possibly run-at values for GM scripts - with corresponding document.readyState in brackets

  • document-start (before document.readyState === 'loading')
  • document-end (document.readyState === 'ineractive')
  • document-idle (document.readyState === 'complete')

These are the only available injection points for your script

at start/end - no external resources will be loaded yet, so too early for what you want

at idle, all external resources are loaded, so too late for what you want

OK, you know all this, but I'm adding it for other future readers

if you load your script at document-end, you can add load listeners to all <link rel="stylesheet" like

Promise.all(Array.from(document.querySelectorAll('link[rel="stylesheet"]'), ss => new Promise(resolve => {
    const href = ss.href;
    const fulfill = status => resolve({href, status});
    setTimeout(fulfill, 5000, 'timeout');
    ss.addEventListener('load', () => resolve('load'));
    ss.addEventListener('error', () => resolve('error')); // yes, resolve, because we just want to wait until all stylesheets are done with, errors shouldn't stop us
}))).then((results) => {
    // results is an array of {href:'some url', status: 'load|error|timeout'}
    // at this point stylesheets have finished loading
});
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • This is a pretty elegant solution. However, it doesn't run if the script runs after a stylesheet has loaded or failed to load. I tested a case where stylesheets are being blocked by uMatrix, and it won't run in that case because the stylesheets are blocked before the user script is run. Is there a way to check if a stylesheet has failed to load or finished loading? – user234683 Oct 23 '18 at 00:21
  • what about `setTimeout` - see edited code @user234683 – Jaromanda X Oct 23 '18 at 00:25
  • @user234683 - I've edited the code a bit, so in the `.then` you can iterate through results to see what loaded and what didn't - perhaps useful for *development* phase of your userscript – Jaromanda X Oct 23 '18 at 00:30
  • My userscript runs on all websites, so I can't make any assumptions about the stylesheets. I guess a timeout will have to do sadly. I'll mark this as the answer in a few days if no one else comes along with ideas for detecting if a stylesheet failed to load. – user234683 Oct 23 '18 at 05:32
  • I would've thought that a failure should trigger an error event - or even a timeout event - what do you see in the console when one of these stylesheets fail? – Jaromanda X Oct 23 '18 at 05:36
  • The problem isn't that an error event isn't being triggered. The problem is that the userscript begins running after such error events happen. It seems the external stylesheet blocking isn't being logged in the console, only external javascript. It is logged to the umatrix logger, however, which gives the time as HH:MM:SS. If I print the time at the beginning of the userscript to the console, I find that the userscript starts in the second after the stylesheets are blocked (So the stylesheets are blocked at 3:43:36 and the userscript starts at 3:43:37) – user234683 Oct 25 '18 at 04:11