Search code examples
javascriptcsscdnconditional-statementsfallback

How to write a fallback for css files not loaded, loading too long and blocked?


I am trying to write a conditional statement for my css files, because I am hoping to host them on CDN. Anyhow, I need to write a fallback for it, so if the CDN encountered a possible downtime, I could run the file right away on my own server. The problem is my knowledge of writing javascript is still a beginner, therefore I have no idea how to write the script.

Can anybody please help me out on this?

The piece code below is one I hope to use upon CDN:

<link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/animatecss/3.2.0/animate.min.css">

Please educate me on how may I create this conditional function properly?


Solution

  • Disclaimer

    Seems like all of these very kind people have marked your question as a duplicate, however they didn't notice that the answer provided to that question does not work (the answer is from 2011 for god's sake, see why it doesn't work in the notes). I've already flagged it to moderators to see if they can unmark that.

    Anyways, I've created a new demo for my updated answer taking into consideration your need to check for multiple cdn css files and how that may affect your websites performance. (I've used the head method in the XMLHttpRequest() which is faster according to this cool guy since it doesn't retrieve the pages body.

    Also I've provided a format to easily replace failed external <link> tags which their local counterpart.

    JavaScript

    CSSback = {
        local: "cssfallback/",
        fallbacks: {
            "animate.min.css": "localanimate.css",
            "nonexistent.min.css": "localnonexistent.css"
        }
    }
    
    CSSback.init = function () {
        var links = document.head.querySelectorAll("link");
        for (var i = 0, l = links.length; i < l; i++) {
            var link = links[i];
            this.ping(link);
        }
    }.bind(CSSback);
    
    CSSback.ping = function (link) {
        var url = link.href;
        var http = new XMLHttpRequest();
        http.onreadystatechange = function () {
            if (http.readyState == 4 && http.status != 200) this.fallback(link);
        }.bind(this);
        http.open("head", url, true);
        http.send();
    }.bind(CSSback);
    
    CSSback.fallback = function (link) {
        var url = link.href.split("/");
        var name = url[url.length - 1];
        var fallback = this.fallbacks[name];
        if (fallback) {
            link.parentNode.removeChild(link);
            var link = document.createElement("link");
            link.rel = "stylesheet";
            link.type = "text/css";
            link.href = this.local + fallback;
            document.head.appendChild(link);
        }
    }.bind(CSSback);
    
    window.addEventListener("load", CSSback.init);
    

    Usage

    You should modify the CSSBack{}'s local (for the path to the local stylesheets) and fallbacks (for a key-value relationship between the name of the external stylesheet, and the name of the local stylesheet).

    HTML

    <link href="http://cdn.jsdelivr.net/animatecss/3.2.0/animate.min.css" (..) >
    <link href="http://cdn.jsdelivr.net/animatecss/3.2.0/nonexistent.min.css" (..) >
    

    Modified JavaScript

    CSSback = {
        local: "cssfallback/",
        fallbacks: {
            "animate.min.css": "localanimate.css",
            "nonexistent.min.css": "localnonexistent.css"
        }
    }
    

    If any <link> fails, it will remove it and replace it with it's local counterpart, in this case nonexistent.min.css will fail and it will be replaced leaving the final <link> tags being:

    <link href="http://cdn.jsdelivr.net/animatecss/3.2.0/animate.min.css" (..) >
    <link href="cssfallback/nonexistent.css" (..) >
    

    Notes

    • A <link> will fail if it's response status is 0 (non-allowed) or 404 (not-found), that means if the stylesheet is online but it's server doesn't have a Access-Control-Allow-Origin="*" policy it will think it failed too. (Fortunately most cdn's have it, including yours).
    • The selected answer in the other question does not work because document.styleSheets.cssRules in external stylesheets returns null in Chrome and Firefox just bluntly throws you a Security Error.

    Hope it helps! :)