Reputation: 10340
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
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
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
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
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
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
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
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
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
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
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