javascriptjquerydocument-readycomputed-stylewindow-load

How to get a computed CSS style before window.load?


I've got a custom slideshow page with hundreds of large pictures in markup. In addition, there is an adjustment to the layout that can't be done with CSS alone, and requires javascript. That adjustment depends on the computed CSS styles of a couple page elements.

Here's the issue: getting a computed CSS style on (document).ready doesn't seem to work. That makes perfect sense, because layout/paint haven't occurred yet when the DOM is merely registered.

(window).load is the obvious answer here. But the waiting period for (window).load is maddeningly long in this case, because it takes literally 30 seconds (longer on slow connections!) for all the 100+ large images to be downloaded. That's fine in principle for the slideshow, because the slideshow starts being usable once the first two images are loaded, long before all the images are loaded. The thing is, I'd like the scripted CSS layout correction to occur at that point as well.

To check for imgs being loaded, I'm use imageLoaded, which is great, and gets around the issues with jquery's load event when used with imgs. But how do I check for "load"-like events (and the availability of computed styles) on a page element, for example a div? (I realize divs don't fire load events.) In principle, is there a reliable cross-browser DOM event in between (document).ready and (window).load that I can listen for on a particular set of elements, to get computed styles? Or am I forced to choose between these two temporal extremes?


Solution

  • On CSS load

    Source: https://stackoverflow.com/a/12570580/1292652

    Add a unique reference styles to the CSS files you want to check for like so:

    #ensure-cssload-0 {
      display: none;
    }
    

    Then, use a JS function, cssLoad(), to repeatedly check whether the CSS has been downloaded (not sure if this is significantly different from when the CSS is actually painted/rendered):

    var onCssLoad = function (options, callback) {
        var body = $("body");
        var div = document.createElement(constants.TAG_DIV);
        for (var key in options) {
            if (options.hasOwnProperty(key)) {
                if (key.toLowerCase() === "css") {
                    continue;
                }
                div[key] = options[key];
            }
        }
    
        var css = options.css;
        if (css) {
            body.appendChild(div);
            var handle = -1;
            handle = window.setInterval(function () {
                var match = true;
                for (var key in css) {
                    if (css.hasOwnProperty(key)) {
                        match = match && utils.getStyle(div, key) === css[key];
                    }
                }
    
                if (match === true) {
                    window.clearTimeout(handle);
                    body.removeChild(div);
                    callback();
                }
            }, 100);
        }
    }
    

    You can use it as:

    onCssLoad({
        "id": <insert element CSS applies to>,
         css: <insert sample CSS styles>
    }, function () {
        console.log("CSS loaded, you can show the slideshow now :)");
    });
    


    On CSS computation

    Source: http://atomicrobotdesign.com/blog/javascript/get-the-style-property-of-an-element-using-javascript/

    The previous solution only told if a CSS file had been 'downloaded', while in your question you mention you need to know when a style for an element has been 'computed' or 'rendered' or 'painted'. I hope this might resolve that.

    Add a unique reference styles to the CSS files you want to check for like so:

    #ensure-cssload-0 {
      ignored-property: 'computed';
    }
    

    Now, use getPropertyValue and getComputedStyle to check for the CSS:

    function getStyle(elem, prop) {
        return window.getComputedStyle(elem, null).getPropertyValue(prop);
    }
    

    Now, just use a while loop with a callback:

    function checkStyle(elem, prop, callback) {
        while ( getStyle(elem, prop) !== 'computed' ) {
            // see explanation below
        }
        callback()
    }
    

    Since JavaScript doesn't have a pass statement (like in Python), we use an empty code block.

    If you want to retrieve all the CSS styles applied to an element (warning: this will also show inherited styles), check out this SO answer.


    Avoiding the while() loop

    Using while loops to check for anything is usually NOT recommended as it hangs uo the browser doesn't let the JavaScript thread do anyting else (all browsers nowadays are multithreaded, see this comic).

    Here's Chuck suggested, this solution is much better:

    function checkStyle(elem, prop, callback) {
        if ( getStyle(elem, prop) !== 'computed' ) {
            // see explanation below
            window.setTimeout( function() {checkStyle(elem, prop, callback);}, 100 )
        } else {
            callback()
        }
    }
    

    This is much better. Now the function asynchronously checks every 100ms (you can change this value if you want). The wrapping of the function while setting the timeout was necessary as setTimeout() doesn't allow passing of any arguments yet.

    Hope this helped. See the source code of this post for some more links...