How to fallback to local stylesheet (not script) if CDN fails

34,070

Solution 1

Not cross-browser tested but I think this will work. Will have to be after you load jquery though, or you'll have to rewrite it in plain Javascript.

<script type="text/javascript">
$.each(document.styleSheets, function(i,sheet){
  if(sheet.href=='http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css') {
    var rules = sheet.rules ? sheet.rules : sheet.cssRules;
    if (rules.length == 0) {
      $('<link rel="stylesheet" type="text/css" href="path/to/local/jquery.mobile-1.0b3.min.css" />').appendTo('head');
    }
 }
})
</script>

Solution 2

One could use onerror for that:

<link rel="stylesheet" href="cdn.css" onerror="this.onerror=null;this.href='local.css';" />

The this.onerror=null; is to avoid endless loops in case the fallback it self is not available. But it could also be used to have multiple fallbacks.

However, this currently only works in Firefox and Chrome.

Update: Meanwhile, this seems to be supported by all common browsers.

Solution 3

Assuming you are using the same CDN for css and jQuery, why not just do one test and catch it all??

<link href="//ajax.googleapis.com/ajax/libs/jqueryui/1/themes/start/jquery-ui.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script type="text/javascript">
    if (typeof jQuery == 'undefined') {
        document.write(unescape('%3Clink rel="stylesheet" type="text/css" href="../../Content/jquery-ui-1.8.16.custom.css" /%3E'));
        document.write(unescape('%3Cscript type="text/javascript" src="/jQuery/jquery-1.6.4.min.js" %3E%3C/script%3E'));
        document.write(unescape('%3Cscript type="text/javascript" src="/jQuery/jquery-ui-1.8.16.custom.min.js" %3E%3C/script%3E'));
    }
</script>

Solution 4

I guess the question is to detect whether a stylesheet is loaded or not. One possible approach is as follows:

1) Add a special rule to the end of your CSS file, like:

#foo { display: none !important; }

2) Add the corresponding div in your HTML:

<div id="foo"></div>

3) On document ready, check whether #foo is visible or not. If the stylesheet was loaded, it will not be visible.

Demo here -- loads jquery-ui smoothness theme; no rule is added to stylesheet.

Solution 5

this article suggests some solutions for the bootstrap css http://eddmann.com/posts/providing-local-js-and-css-resources-for-cdn-fallbacks/

alternatively this works for fontawesome

<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<script>
    (function($){
        var $span = $('<span class="fa" style="display:none"></span>').appendTo('body');
        if ($span.css('fontFamily') !== 'FontAwesome' ) {
            // Fallback Link
            $('head').append('<link href="/css/font-awesome.min.css" rel="stylesheet">');
        }
        $span.remove();
    })(jQuery);
</script>
Share:
34,070

Related videos on Youtube

ssn
Author by

ssn

Updated on July 08, 2022

Comments

  • ssn
    ssn almost 2 years

    I am linking to the jQuery Mobile stylesheet on a CDN and would like to fall back to my local version of the stylesheet if the CDN fails. For scripts the solution is well known:

    <!-- Load jQuery and jQuery mobile with fall back to local server -->
    <script src="http://code.jquery.com/jquery-1.6.3.min.js"></script>
    <script type="text/javascript">
      if (typeof jQuery == 'undefined') {
        document.write(unescape("%3Cscript src='jquery-1.6.3.min.js'%3E"));
      }
    </script>
    

    I would like to do something similar for a style sheet:

    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css" />
    

    I am not sure if a similar approach can be achieved because I am not sure whether the browser blocks in the same way when linking a script as it does when loading a script (maybe it is possible to load a stylesheet in a script tag and then inject it into the page) ?

    So my question is: How do I ensure a stylesheet is loaded locally if a CDN fails ?

    • Fosco
      Fosco almost 13 years
      I'd like to know if this is possible as well... If I really fretted about the CDN being down, I would just use local hosting.
    • Shawn Mclean
      Shawn Mclean almost 13 years
      @Stefan Kendall, i think the right statement is that his site will more than likely to go down than a CDN
    • nmit026
      nmit026 over 7 years
  • Admin
    Admin almost 13 years
    Yes, at least in modern browser, I am not sure about IE6.
  • Shawn Mclean
    Shawn Mclean almost 13 years
    Is there a way to just check instead of downloading the whole thing?
  • Stefan Kendall
    Stefan Kendall almost 13 years
    The only possible reason to do the OPs request is to avoid excess network traffic. This creates excess network traffic.
  • Admin
    Admin almost 13 years
    @stefan Kendall: no that is not even the possible reason he wan'ts to make sure that the files get loaded.
  • Stefan Kendall
    Stefan Kendall almost 13 years
    If that was the only concern, you would just not use a CDN. Testing just the header is better, but I'm pretty sure most CDNs and your browser aren't going to allow XSS.
  • Admin
    Admin almost 13 years
    Sure thing not using a CDN is the safest way but I give the answer assuming that the OP has some bandwith/speed issue.
  • Admin
    Admin almost 13 years
    @stefan Kendall: I am wondering if you down-voted because the answer is not the best for your assumption of OP situation but not the actual question.
  • Stefan Kendall
    Stefan Kendall almost 13 years
    @Omeid: I explained why this probably won't work. Go try and create a jsfiddle testing this. Also, this doesn't account for when the CDN returns 503s, or when it returns 200s with empty content. There are many "potential edge cases" this doesn't cover, and it's almost certainly better handled at the CDN level anyway.
  • Admin
    Admin almost 13 years
    @stefan: the OP asked to check for the CSS if its loaded or not and then to decide to download it from its server and it actually works.
  • GeorgeU
    GeorgeU almost 13 years
    good solution, one issue it does not address is if the CDN is way too slow to load... maybe some sort of timeout?
  • Jannie Theunissen
    Jannie Theunissen almost 12 years
    +1 for a clever solution. Only problem is, one normally can't go and add a line to the end of a style sheet that is hosted on someone's CDN
  • simplfuzz
    simplfuzz about 11 years
    For code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css, I get rules = null, even though it's been loaded properly. I am using Chrome 26 and I think it's because the script is cross domain?
  • icebreaker
    icebreaker about 11 years
    CDN can be reliable, but not the development environment internet connection ;)
  • AlicanC
    AlicanC over 10 years
    @BenSchwarz, that doesn't mean you can paste some irrelevant code which in no way answers the asked question.
  • Maksim Vi.
    Maksim Vi. over 10 years
    The solution doesn't really work for all CDNs/Stylesheets, for example CSSStyleSheet js objects that come from bootstrapcdn.com all have empty rules and cssRules fields in my browser (Chrome 31). UPD: it actually might be a crossdomain issue, css file in the answer also doesn't work for me.
  • Ahamed
    Ahamed about 10 years
    Unfortunately, we can't type in our own classes to CDN files. May be we can try to utilize the one that exists already.
  • Hoppe
    Hoppe over 9 years
    Does this work with the protocol-less syntax? I.e. href="//ajax.googleapis.com/ajax/libs/jqueryui/1/themes/star‌​t/jquery-ui.css"
  • Nosnibor
    Nosnibor over 9 years
    I like this one A LOT, thank you. It's quite powerful really. Obviously I cannot manipulate the CDN stylesheet but I know what classes are being used so I amended the code to show check if they are visible - very clever indeed :)
  • Jack
    Jack over 9 years
    May I ask what the issue with using unescaped strings initially, e.g. document.write("<script type='text/javascript' src='path/to/file.js'>")?
  • Brad Christie
    Brad Christie over 9 years
    @JackTuck: The parser can't differentiate between <script> inside a JS string and one found outside. This is commonly why you also see <\/script> when writing out tags for CDN fallbacks.
  • Flimzy
    Flimzy almost 9 years
    -1 why not just do one test and catch it all? -- Because there are a million reasons that one might fail, and the others succeed.
  • Chemical Programmer
    Chemical Programmer almost 9 years
    This is also good soloution using onerror event. stackoverflow.com/questions/30546795/…
  • John Vinopal
    John Vinopal almost 9 years
    I like the encapsulation, but in general you can't inspect sheet.rules for a cross-domain stylesheet. You can still use this general idea but need to do a different check.
  • Salman A
    Salman A over 7 years
    Does it work for CSS loaded from another domain? No, you cannot enumerate .rules/.cssRules for external stylesheets. jsfiddle.net/E6yYN/13
  • Salman A
    Salman A over 7 years
    NB: you do not really have to add a new rule to the external CSS. Just use an existing rule whose behavior is known. In my demo I use ui-helper-hidden class which is supposed to hide the element, i then check if the element gets hidden on page load.
  • Jason Clark
    Jason Clark about 6 years
    For those looking to use this with Font Awesome 5, you'll want to change 'FontAwesome' (in the if clause) to 'Font Awesome 5 Free' (if you're using the free fonts). Otherwise, it should work fine.
  • Alwin Kesler
    Alwin Kesler over 5 years
    with document.styleSheets[i].ownerNode.dataset you can access <link data-* /> attributes
  • fabio
    fabio almost 4 years
    Can't you check for the name or href? If so, how?
  • Jerry Krinock
    Jerry Krinock almost 4 years
    CONGRATULATIONS to all of you who, like me, couldn't believe that the complicated schemes proposed in earlier answers could still be the best way, and made it all the way down to here. This half-of-a-one-liner works perfectly. Let us see how fast we can up-vote it to the top. Or maybe someone with moderator access can delete the old answers.
  • Connor Low
    Connor Low about 3 years
    We don't delete old answers for being "not as elegant", but this is a good answer all the same.
  • Mafii
    Mafii over 2 years
    I searched for so long. This is great! How was this so hard to find. Thank you so much!
  • G M
    G M about 2 years
    However, sometimes the CDN just doesn't respond so you don't have any error and just keep waiting....