How to Determine If CSS Has Been Loaded

How to determine if CSS has been loaded?

After doing some research and writing up my answer, I stumbled upon this link that explains everything you need to know about CSS, when it is loaded and how you can check for it.

The link provided explains it so well, in fact, that I'm adding some quotes from it for future reference.

If you're curious, my answer was going to be #2 and a variation of #4.

When is a stylesheet really loaded?

...

With that out of the way, let's see what we have here.

// my callback function 
// which relies on CSS being loaded function
CSSDone() {
alert('zOMG, CSS is done');
};

// load me some stylesheet
var url = "http://tools.w3clubs.com/pagr/1.sleep-1.css",
head = document.getElementsByTagName('head')[0],
link = document.createElement('link');

link.type = "text/css";
link.rel = "stylesheet";
link.href = url;

// MAGIC
// call CSSDone() when CSS arrives
head.appendChild(link);

Options for the magic part, sorted from nice-and-easy to ridiculous

  1. listen to link.onload
  2. listen to link.addEventListener('load')
  3. listen to link.onreadystatechange
  4. setTimeout and check for changes in document.styleSheets
  5. setTimeout and check for changes in the styling of a specific element you create but style with the new CSS

5th option is too crazy and assumes you have control over the content of the CSS, so forget it. Plus it checks for current styles in a timeout meaning it will flush the reflow queue and can be potentially slow. The slower the CSS to arrive, the more reflows. So, really, forget it.

So how about implementing the magic?

// MAGIC 

// #1
link.onload = function () {
CSSDone('onload listener');
};

// #2
if (link.addEventListener) {
link.addEventListener('load', function() {
CSSDone("DOM's load event");
}, false);
};

// #3
link.onreadystatechange = function() {
var state = link.readyState;
if (state === 'loaded' || state === 'complete') {
link.onreadystatechange = null;
CSSDone("onreadystatechange");
}
};

// #4
var cssnum = document.styleSheets.length;
var ti = setInterval(function() {
if (document.styleSheets.length > cssnum) {
// needs more work when you load a bunch of CSS files quickly
// e.g. loop from cssnum to the new length, looking
// for the document.styleSheets[n].href === url
// ...

// FF changes the length prematurely :(
CSSDone('listening to styleSheets.length change');
clearInterval(ti);
}
}, 10);

// MAGIC ends

How to use Javascript to check and load CSS if not loaded?

Just check to see if a <link> element exists with the href attribute set to your CSS file's URL:

if (!$("link[href='/path/to.css']").length)
$('<link href="/path/to.css" rel="stylesheet">').appendTo("head");

The plain ol' JS method is simple too, using the document.styleSheets collection:

function loadCSSIfNotAlreadyLoadedForSomeReason () {
var ss = document.styleSheets;
for (var i = 0, max = ss.length; i < max; i++) {
if (ss[i].href == "/path/to.css")
return;
}
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = "/path/to.css";

document.getElementsByTagName("head")[0].appendChild(link);
}
loadCSSIfNotAlreadyLoadedForSomeReason();

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

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

Detect when css is applied

Never tried before, but what about Stylesheet load events? In case IE won't fired the onload event, and you need to support it, onreadystatechange should works.

Check css file loaded or not using document.styleSheets

I tried your code ant it works for me, ( on chrome, with internal css ), but it fails when using external css loaded from CDN's, so i would guess your problem is "rules" property as mentioned by @PatrickEvans.

If you don't find any other good way, then you may add an element to the page that doesn't affect the page display, but can be checked for a change.

e.g add a specific css rule like this.

html body div#my_stylesheet_name {
width: 112px !important;//random width that is unlikely overwritten by another css
}
<div id="my_stylesheet_name" style="height:0; width: 0;display: none;"></div>

//then use javascript timer/interval to check if
element with id of "my_stylesheet_name" has width of 112, if so, then it means css has loaded.

Edit - If you dont have any other option, you may consider something like this ( i havent used it myself before so test browser compatibility before using it )

1) create the element using JS

2) add on error and onload events, and use those to do your magic

    var link;
link = document.createElement("link");
link.setAttribute("type", "text/css");
link.onload=function( evt ) {
console.log("LINK LOADED", evt );
};
link.onerror=function( evt ) {
console.log("LINK Error", evt );
};
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css");
document.getElementsByTagName("head")[0].appendChild(link);

Detect style sheet load

  1. <link> elements have load and error events, but they are garanteed only for Chrome, Firefox and Opera. If you don't need to support IE or Safari, I think this is just enough:

    <link rel="stylesheet" href="some_style.css" onload="anounceReady()">

    EDIT: Just tested with IE11 and IE8, onload is supported but onerror isn't. I have no Mac at hand so I don't know how Safari handles them.

  2. If you are going to support browsers don't have load event for <link> elements, or very old Chrome/Firefox, there is a commonly used easy dirty but working solution. Add a piece of probing rule set at the end of your CSS file, and detect the computed style of a certain probing element (usually invisible) via JavaScript.

    HTML:

    <div id="probe"></div>

    CSS:

    #probe {
    position: absolute;
    z-index: -1000;
    }

    JavaScript:

    var probeStyle = getComputedStyle(document.getElementById('probe'));
    probeStyle.zIndex; // expect "-1000" after CSS is loaded

How to determine when document has loaded after loading external css

I wrote this code, what i wanted and worked for me:

window.engineLoading = {images_count:0, images_loaded_count:0, fonts_count:0, fonts_loaded_count:0 };
document.querySelector("a").onclick = function(){ // first elemnet a
var before_stylesheets_length = document.styleSheets.length;
var before_fonts_size = document.fonts.size;

document.fonts.onloadingerror = function(a){
window.engineLoading.fonts_loaded_count++;
}
document.fonts.onloading = function(a){
window.engineLoading.fonts_count++;
}
document.fonts.onloadingdone = function(a){
window.engineLoading.fonts_loaded_count++;
}

var head= document.getElementsByTagName('head')[0];
var style= document.createElement('link');
style.rel= 'stylesheet';
style.setAttribute("href","./new_style.css");
style.onload = function(){
for(i=before_stylesheets_length; i<document.styleSheets.length; i++){
var rules = document.styleSheets[i].rules;
for(q=0; q<rules.length; q++){
var styles = rules[q].style;
for(s=0; s<styles.length; s++){
console.log(styles[s]);
if((styles[s] == "background-image" || styles[s] == "background") && styles.backgroundImage.length > 0){
window.engineLoading.images_count++;
var body= document.getElementsByTagName('body')[0];
var image = document.createElement('img');
var url = styles.backgroundImage;
url = url.replace(/^url\(["']?/, '').replace(/["']?\)$/, '');
image.src = url;
image.width = 0;
image.height = 0;
image.setAttribute("class","pace-load-style");
image.onload = function(e){
console.log(e);
window.engineLoading.images_loaded_count++;
};
image.onerror = function(e){
window.engineLoading.images_laoded_count++;
}
body.appendChild(image);
break;
}
}
}
}
};
style.onerror = function(){};
head.appendChild(style);


setTimeout(function(){
checkCurrentState();
}, 1000);
return false;
};

function checkCurrentState(){
if(window.engineLoading.images_count == window.engineLoading.images_loaded_count && window.engineLoading.fonts_count == window.engineLoading.fonts_loaded_count){
console.log("loaded"); return true;
}console.log("still loading...");
return setTimeout(function(){
checkCurrentState();
}, 1000);
};

UPDATE: Scipt has bug on localfile because of empty rule. CSSRules is empty I don't worry about it , and no need fix it.

UPDATE: Mozilla Firefox hasnt reference document.fonts.

How can I tell if a CSS file has loaded that I've included via JavaScript?

I don't know about triggering an event, but can you make an get request for the CSS file and then just update the <head> with a new <style> tag, placing the content of the file inside the <style> tag?

$(document).ready(function() { 
$.get("http://example.com/style.css", function(data) {
$('head').append("<style>" + data + "</style>");
});
... /* your code */ ...
});

How to check if all CSS files have been downloaded to insert HTML using JavaScript?

You could use YepNope.js, YepNope allows you to build asynchronous conditional tests to see whether resources have loaded. Once your tests have passed you can tell it to inject new CSS or JS files.

Example below has been taken from the YepNope.js site.

yepnope.injectCss( stylesheetSource [, callback ] [, elemAttributes ] [, timeout ]);

// Example
yepnope.injectCss("print.css", function () {
console.log("css injected!");
}, {
media: "print"
}, 5000);

You can even make YepNope load the initial CSS files first and then once they have completed loading YepNope can trigger a callback to do additional tasks, such as loading more JS or CSS files.



Related Topics



Leave a reply



Submit