How to Fallback to Local Stylesheet (Not Script) If Cdn Fails

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

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>

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

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! :)

How to load local script files as fallback in cases where CDN are blocked/unavailable?

To confirm that cdn script loaded you can check for existence any variable/function this script defines, if it is undefined - then cdn failed and you need to load local script copy.

On this principle are based solutions like that:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.5.1.min.js">\x3C/script>')</script>

(if there is no window.jQuery property defined cdn script didn't loaded).

You may build your own solutions using this method. For instance, jquery tooltip plugin creates $.tooltip() function so we can check it with code like this:

<script>
if (typeof $.tooltip === 'undefined') {
document.write('<script src="js/libs/jquery.tooltip.min.js">\x3C/script>');
}
</script>

Provide local fallback for CSS from CDN

This is what I have created for our needs. If this meets your needs, simply call the function ensureCssFileInclusion(file to check, boolean value). You will have to adjust it for your needs to make sure that you provide the cssFileToCheck, fallbackCssFile in this function.

/**
* Checks the page for given CSS file name to see if it was already included within page stylesheets.
* If it was, then this function does nothing else. If CSS file was not found among page stylesheets,
* then this function will attempt to load the stylesheet by adding an HTML link tag to the document
* HEAD section. You must also specify whether given cssFileToInclude is a relative path or an absolute path.
*/
ensureCssFileInclusion = function(cssFileToInclude, isRelativePath) {
if (isRelativePath) {
if (!window.location.origin) {
cssFileToInclude = window.location.protocol+"//"+window.location.host + cssFileToInclude;
}
}
var styleSheets = document.styleSheets;
for (var i = 0, max = styleSheets.length; i < max; i++) {
if (styleSheets[i].href == cssFileToInclude) {
return;
}
}
// because no matching stylesheets were found, we will add a new HTML link element to the HEAD section of the page.
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = cssFileToInclude;
document.getElementsByTagName("head")[0].appendChild(link);
};

How to create fallback for CDN libraries without using document.write()

I recommend using 3p packages like fallback.js or require.js given they are more scalable in case you have multiple fallbacks and they give you faster loading performance.

Example of fallback.js

HTML CODE

<html>
<head>
<!-- **The `data-main` attribute tells the library to load `main.js`** -->
<script async data-main="main" src="fallback.min.js" type="text/javascript"></script>
</head>

<body>
</body>
</html>

MAIN JS FILE

cfg({
"libs": {
"jQuery": {
"urls": [
"//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min",
"//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min"
]
},
}
});

req(function(jQuery) {
jQuery("body");
});

Example of require.js

requirejs.config({
enforceDefine: true,
paths: {
jquery: [
'https://code.jquery.com/jquery-3.4.1.min.js',
//If the CDN location fails, load from this location
'js/jquery-3.4.1.min.js'
]
}
});

require(['jquery'], function ($) {});


Related Topics



Leave a reply



Submit