Lukas
Lukas

Reputation: 10340

Is there any way to detect when a CSS file has been fully loaded?

I've been testing a lot of lazy-loaders for JavaScript and CSS that insert <script> and <link> tags to load files. However the problem is, that <link> tags don't fire onload so it's difficult to detect when they're loaded. The only workaround I found for this is to set display: none; (in the CSS file that is to be loaded) on a dummy element and poll that element to check when it has been set to display: none. But that, apart from being ugly, of course only works for a single CSS file.

So I was wondering; Is there any other way to detect if a CSS file has been loaded?

Upvotes: 41

Views: 27227

Answers (10)

Johhn
Johhn

Reputation: 1039

I was using a tailwindcss cdn -> <script src="https://cdn.tailwindcss.com"></script>. Now, I had this at the bottom in the body after everything else but what I wanted was to have the css loaded first and thus all I had to do was to add the above script in the head tags just before the body is loaded.

Upvotes: 0

Frane Poljak
Frane Poljak

Reputation: 2365

This is by far the simplest and best solution I have found so far and it works perfectly:

var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = css_url;

head.appendChild(link);

var img = document.createElement('img');
document.body.appendChild(img);

img.onerror = img.onload = function() {
    img.onerror = img.onload = null;
    document.body.removeChild(img);
    // do whatever you need to do when css loaded;
};

img.src = css_url;

Upvotes: 2

Hung Pham
Hung Pham

Reputation: 223

try this: 1- preload css on head

<link rel="preload" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" as="style" onload="this.rel='stylesheet'"> 
<link rel="preload" href="style.css" as="style" onload="this.rel='stylesheet'"> 

2 - preload js file on footer

    <link rel="preload" as="script" href="https://ajax.googleapis.com/ajax/libs/jquery/2.2.1/jquery.min.js">
    <link rel="preload" as="script" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">
    <link rel="preload" as="script" href="script.js">

3 - before tag </body> add

<script>        
/**  load defer css - js*/  
// defer css
var cssAll = document.querySelectorAll('[as="style"]');
for(i = 0; i < cssAll.length; i++){
    if(cssAll[i].getAttribute("rel") == "preload"){         
        cssAll[i].setAttribute("rel", "stylesheet");
    }
}
//defer js
var jsAll = document.querySelectorAll('[as="script"]');
if(!window.jQuery)
{
    // load jquery lib
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src = jsAll[0].getAttribute("href");
    document.getElementsByTagName('head')[0].appendChild(script);
   // load other lib after load jquery
    script.onload = function () {
        for(i = 1; i < jsAll.length; i++){
            var jsNext = document.createElement('script');
            jsNext.type = "text/javascript";
            jsNext.src = jsAll[i].getAttribute("href");
            document.getElementsByTagName('head')[0].appendChild(jsNext);
        }
    }   
}
</script>   

I tested on firefox 57, chrome 61, yandex, not tested on opera and ie

Upvotes: 2

Steven Nelson
Steven Nelson

Reputation: 83

I wanted to offer some insight into what may have changed.

According to one of the last paragraphs on Mozilla's documentation of <link>, a load event can be attached to a link element in FF versions 9 and up as well as Chrome 19 and up. IE and Safari are not stated. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link

Upvotes: 2

eriksv88
eriksv88

Reputation: 3528

Edit: (Because of the possible not support WebKit)

So I'd rather recommend JQuery LOADER

$("a.button, input.button, button.button").Loader(
{
    url: [
        'core.css',
        'theme.css',
        'button.css'
    ],
    success: function() {
        $(this).button();
    }
});

You can take a look at LazyLoad JavaScript library.

LazyLoad is a tiny (only 1,541 bytes minified), dependency-free JavaScript library that makes it super easy to load external JavaScript and (new in this version) CSS files on demand. It’s ideal for quickly and unobtrusively loading large external scripts and stylesheets either lazily after the rest of the page has finished loading or on demand as needed.

In addition to CSS support, this version of LazyLoad also adds support for parallel loading of multiple resources in browsers that support it. To load multiple resources in parallel, simply pass an array of URLs in a single LazyLoad cal

Upvotes: 4

Lukas
Lukas

Reputation: 10340

edit: It should be noted that browser support for onload events on CSS files has improved since my original answer. It is not fully supported though, so my answer below still has some relevance. Here is a compatibility chart, not sure how legit the source is though.

Ok, I finally found a solution.

This guy http://tugll.tugraz.at/96784/weblog/9080.html inserts link-tags and polls document.styleSheets[index].rules until it is no longer undefined (where index of course is the index of the newly inserted file). Unfortunately his code is buggy and only works with Safari & FF. So I fixed the bugs, added functionality for Opera and Internet Explorer and even added features for adding multiple CSS and JS files and 1 final callback (when all files are loaded) in a sweet and simple lazyloader-function. The result can be found here:

https://github.com/LukasBombach/Lazyloader

Upvotes: 17

Ben
Ben

Reputation: 2045

Worth knowing that now current versions of Chrome and Firefox fire the onload event on links.

var cssFile = document.createElement("link");
cssFile.setAttribute("rel", "stylesheet");
cssFile.setAttribute("type", "text/css");
// Add event listener
cssFile.onload = function(){ console.log('loaded'); }
cssFile.setAttribute("href", 'pathToYour.css');
document.getElementsByTagName("head")[0].appendChild(cssFile);

Upvotes: 6

Nicolas
Nicolas

Reputation: 1679

About the "load-checking CSS rules" :

If, in your JS script, you append in your head a style tag containing a simple rule like : #loadingCss { display: block; }

Than, you just have to add in all your CSS files something like : #loadingCss { display: none; }

In the head of your doc you append the style tag before the link tag. This way, the CSS rule in the CSS file will be more important (same selector -> same priority, the last one in the doc is the winner).

Than, in your JS script you just have to check #loadingCss visibility. Once you know your CSS file is loaded, you can remove the style tag.

For the next CSS file you want to load, you can add this style tag again at the end of the head element.

This way, with only one rule, you can manage all your CSS files loading. The only problem with this solution : You can't load more than one CSS file at a time. But I'm sure it's possible to find a way to do so.

But anyway, I'm not sure this solution (using a CSS rule to check loading) is a very good solution. Maybe there's other ways to do this.

Upvotes: 1

jAndy
jAndy

Reputation: 235962

Using a script loader like Supply, this would look like:

supply.listen('text/css', function(payload, filename) {
    switch(filename) {
        case: 'foo.css': {
            // do something
            break;
        }
        case: 'baseball.css': {
            break;
        }
        // ...
    }
});

supply.files({
    stylesheet: [
        'foo.css',
        'baseball.css'
    ]
});

Ref.: SupplyJS

Upvotes: 1

Daniel B&#246;hmer
Daniel B&#246;hmer

Reputation: 15381

Try to add a certain CSS rule to the end of your file and wait for the CSS to be applied (check that via JavaScript). After that you can be pretty sure the CSS has been loaded. However I have no experience with that. Just a quick idea might be worth a try.

Upvotes: 3

Related Questions