Search code examples
javascriptgreasemonkeygreasemonkey-4

greasemonkey - run script when html and css is loaded


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?


Solution

  • 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
    });