There's a similar post already, but I don't feel it answers my particular question well enough.
I want to check that an external stylesheet has loaded. For example, if it has been stymied by a network problem then I want to know and load a fallback. This is as a fallback for a CDN for jQuery's themes CSS but I'd prefer this doesn't end up being specifically about that as I've other external CSS I use that I'd like to apply this to as well.
- The reference
link
element will be there on the page, so pattern matching for the existence of the link element in the head is not acceptable. - I do not have the ability to change the CSS in the stylesheet, so adding dummy CSS is not acceptable.
- Since I'm not in control of the CSS in the external stylesheet, relying on existing classes in the CSS is fragile, so I'd prefer if there is another solution.
- Is using a timeout the only way? How long should I set for it? What event should I choose to measure the time?
- I'd prefer pure javascript, then jQuery, but answers for other javascript frameworks will be accepted since they'll no doubt be helpful to someone.
Edit: A new thought. What about if the stylesheet was loaded via AJAX, and the status code checked? Is that a viable option, does anyone know?
Here's the script I have currently:
<script type="text/javascript">
var cdn = '.10.1/themes/black-tie/jquery-ui.min.css';
var ss = document.styleSheets;
for (var i = 0, max = ss.length; i < max; i++) {
var sheet = ss[i];
var rules = sheet.rules ? sheet.rules : sheet.cssRules;
if (sheet.href == cdn) {
if ( rules == null || rules.length == 0) {
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = '/js/jquery-ui/1.10.1/themes/black-tie/jquery-ui.min.css';
document.getElementsByTagName("head")[0].appendChild(link);
}
break;
}
}
</script>
What I've found on using the console (Chrome v25.0.1364.172) is that even when the stylesheet has loaded from the CDN, document.styleSheets[x].rules
is null
. I'm not sure if I'm accessing it correctly in the console though, maybe there's another function I should use or object?
CSSStyleSheet {rules: null, cssRules: null, ownerRule: null, media: MediaList, title: null…}
cssRules: null
disabled: false
href: ".10.1/themes/black-tie/jquery-ui.min.css"
media: MediaList
ownerNode: link
ownerRule: null
parentStyleSheet: null
rules: null
title: null
type: "text/css"
__proto__: CSSStyleSheet
If anyone can help me find how to check the stylesheet has loaded, that would be very helpful and much appreciated.
There's a similar post already, but I don't feel it answers my particular question well enough.
I want to check that an external stylesheet has loaded. For example, if it has been stymied by a network problem then I want to know and load a fallback. This is as a fallback for a CDN for jQuery's themes CSS but I'd prefer this doesn't end up being specifically about that as I've other external CSS I use that I'd like to apply this to as well.
- The reference
link
element will be there on the page, so pattern matching for the existence of the link element in the head is not acceptable. - I do not have the ability to change the CSS in the stylesheet, so adding dummy CSS is not acceptable.
- Since I'm not in control of the CSS in the external stylesheet, relying on existing classes in the CSS is fragile, so I'd prefer if there is another solution.
- Is using a timeout the only way? How long should I set for it? What event should I choose to measure the time?
- I'd prefer pure javascript, then jQuery, but answers for other javascript frameworks will be accepted since they'll no doubt be helpful to someone.
Edit: A new thought. What about if the stylesheet was loaded via AJAX, and the status code checked? Is that a viable option, does anyone know?
Here's the script I have currently:
<script type="text/javascript">
var cdn = 'http://code.jquery./ui/1.10.1/themes/black-tie/jquery-ui.min.css';
var ss = document.styleSheets;
for (var i = 0, max = ss.length; i < max; i++) {
var sheet = ss[i];
var rules = sheet.rules ? sheet.rules : sheet.cssRules;
if (sheet.href == cdn) {
if ( rules == null || rules.length == 0) {
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = '/js/jquery-ui/1.10.1/themes/black-tie/jquery-ui.min.css';
document.getElementsByTagName("head")[0].appendChild(link);
}
break;
}
}
</script>
What I've found on using the console (Chrome v25.0.1364.172) is that even when the stylesheet has loaded from the CDN, document.styleSheets[x].rules
is null
. I'm not sure if I'm accessing it correctly in the console though, maybe there's another function I should use or object?
CSSStyleSheet {rules: null, cssRules: null, ownerRule: null, media: MediaList, title: null…}
cssRules: null
disabled: false
href: "http://code.jquery./ui/1.10.1/themes/black-tie/jquery-ui.min.css"
media: MediaList
ownerNode: link
ownerRule: null
parentStyleSheet: null
rules: null
title: null
type: "text/css"
__proto__: CSSStyleSheet
If anyone can help me find how to check the stylesheet has loaded, that would be very helpful and much appreciated.
Share Improve this question edited May 23, 2017 at 12:17 CommunityBot 11 silver badge asked Jul 16, 2013 at 1:54 ianian 12.3k9 gold badges54 silver badges112 bronze badges 4- 1 Well in your case you want to know if the sheet is actually loaded, whereas the post you linked to simply deals with placing a <link> in the header if one doesn't exist already. Unfortunately there's no method to check via Javascript if a <link> element has finished loading, even if you create it via JS. I did e up with this: blog.rjzaworski./2011/08/… which is a little bit hackish, but in short it involves placing an element on the page whose attributes would change once the CSS is loaded, then checking it at intervals. – Brian Commented Jul 16, 2013 at 2:02
- @Brian thanks for that, it's an interesting approach. I think you should add this as an answer so I (and others) can at the very least give you some kudos for helping. You're too modest :) – ian Commented Jul 16, 2013 at 6:44
- Well it's not a plete solution because there's not really any way of knowing when the CSS is done loading to the browser. So you'd have to set a somewhat arbitrary "timeout" period after which you decide to use your fallback CSS. These sorts of issues are typically handled by way of load balancers and CDNs anyway, not by JS code in the front end. I don't think what I've contributed here is strong enough to provide as an "answer" per se, but rather just a point in the general direction you may want to head to solve this using JS code ;) – Brian Commented Jul 16, 2013 at 12:05
- @Brian I finally got it to work, using a version of the ideas from your blog post. Thanks very much, I really appreciate it. – ian Commented Aug 6, 2013 at 22:40
1 Answer
Reset to default 5Note:
See the production code at the github repo for the rack-jquery_ui-themes library. Ignore all interpolation of constants and the uppercase symbols like :THEME
, they get substituted elsewhere. Just imagine it's pristine javascript.
What Worked
Many thanks to Brian in the ments to my question for providing the main source of inspiration for this.
<meta id='rack-jquery-ui-themes-fallback-beacon' />
<script type="text/javascript">
var meta = $("#rack-jquery-ui-themes-fallback-beacon");
meta.addClass("ui-icon");
if ( meta.css('width') != "16px" ) {
$('<link rel="stylesheet" type="text/css" href="/js/jquery-ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/#{JQUERY_UI_THEME_FILE}" />').appendTo('head');
}
meta.remove();
</script>
Adding a meta tag, then applying a CSS class from the jQuery UI stylesheet, then checking it had changed and adding the fallback link if not, then removing the meta tag.
There is a reason we use a meta tag in the head instead of something like a div in the body. Most CSS files are included by link elements in the head. If the script to check if it loaded es immediately after that, since it is in the context of the head, the body has not been loaded into the DOM yet. Accessing document.body in that context will cause a null ref error.
What Didn't Work
Using AJAX to make a HEAD request and checking the status:
<script type="text/javascript">
$.ajax({
type: "HEAD",
url: ':CDNURL',
success: function(css,status) {
// do nothing
},
error: function(xhr,status,error) {
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = ':FALLBACK_URL';
document.getElementsByTagName("head")[0].appendChild(link);
}
});
</script>
I found out that this is a potential security threat so browsers won't allow it.
Checking the DOM for stylesheet rules:
<script type="text/javascript">
var has_jquery_rules = false;
var i = document.styleSheets.length - 1;
while (i >= 0 ) {
var sheet = document.styleSheets[i];
if(sheet.href == ":CDNURL/ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/jquery-ui.min.css") {
var rules = sheet.rules ? sheet.rules : sheet.cssRules;
has_jquery_rules = rules.length == 0 ? false : true;
break; // end the loop.
}
has_jquery_rules = false;
i--;
}
if ( has_jquery_rules == false ) {
$('<link rel="stylesheet" type="text/css" href="/js/jquery-ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/#{JQUERY_UI_THEME_FILE}" />').appendTo('head');
}
</script>
This also doesn't work, as some (all?) browsers block access to rules added by an external stylesheet.