1028

What would be a good way to attempt to load the hosted jQuery at Google (or other Google hosted libs), but load my copy of jQuery if the Google attempt fails?

I'm not saying Google is flaky. There are cases where the Google copy is blocked (apparently in Iran, for instance).

Would I set up a timer and check for the jQuery object?

What would be the danger of both copies coming through?

Not really looking for answers like "just use the Google one" or "just use your own." I understand those arguments. I also understand that the user is likely to have the Google version cached. I'm thinking about fallbacks for the cloud in general.


Edit: This part added...

Since Google suggests using google.load to load the ajax libraries, and it performs a callback when done, I'm wondering if that's the key to serializing this problem.

I know it sounds a bit crazy. I'm just trying to figure out if it can be done in a reliable way or not.


Update: jQuery now hosted on Microsoft's CDN.

http://www.asp.net/ajax/cdn/

Chris Morgan
  • 73,264
  • 19
  • 188
  • 199
Nosredna
  • 74,873
  • 15
  • 91
  • 122
  • 9
    Of course the first answer was "don't use the google hosted version." :-) – Nosredna Jun 18 '09 at 17:59
  • 7
    Of course it was because if you want to host a serious web site, you dont rely on someone else hosting your files. – Bryan Migliorisi Jun 18 '09 at 18:09
  • 6
    @Bryan Migliorisi, I guess Twitter is not that serious after all? But I admit they had their problems with Google like a month ago when Google went down. – Ionuț G. Stan Jun 18 '09 at 18:11
  • 19
    The merits of using Google or not for JS lib hosting is a worthy one, but it's been discussed in several other threads. I was looking for technical answers regarding JS fallback on loading delays. – Nosredna Jun 18 '09 at 18:18
  • 1
    Why even use the hosted version if you have a local copy? – Joe Chung Jul 26 '09 at 08:29
  • 2
    @Joe Chung: It's likely to be cached on the user's system, speeding up page load. Saves me bandwidth. Uses Google's CDN. Etc. – Nosredna Jul 26 '09 at 16:32
  • @Joe : http://stackoverflow.com/questions/313409/streaming-jqueryjs-files-from-a-cdn-google – Cherian Jul 28 '09 at 04:09
  • 1
    Another reason for this is that it lets you hack on your webapp locally even when you're offline. – Marius Gedminas Jan 02 '11 at 17:31
  • The main idea behind these answer is "did jQuery fail to load? if so, http://stackoverflow.com/questions/950087/include-javascript-file-inside-javascript-file " – ninjagecko Jun 19 '11 at 06:38
  • maybe it would also be good to host it from google, if it fails, host it from microsoft, and if they both fail, open the local file? – Ruben Apr 02 '12 at 15:05
  • For academic purposes, we can always check for jquery first to see if it's in the cache before loading it from google. If checking if it's in the cache, it's also worthwhile to see if the query version loaded will support your code. If not load the appropriate version in no conflict. *Is that right? – David Hobs Apr 25 '12 at 01:26
  • 1
    This is useful if one is developing a web app which might have to run in an intranet which has no connection to the internet. – MauganRa Sep 28 '12 at 16:35
  • 2
    @MauganRa .. And also in CHINA http://stackoverflow.com/questions/19943588/is-google-web-fonts-or-cdn-bad – Obmerk Kronen Nov 16 '13 at 05:14
  • Is having so many calls to cdn like `https://ajax.googleapis` matter, i mean i read that there is limit in webbrowser to make number of requests at same time for same domain ? – shareef Nov 15 '18 at 09:11
  • The best is to fallback to another CDN, like in https://stackoverflow.com/a/56712767/369005 – Hamid Sarfraz Jul 13 '19 at 05:54

23 Answers23

818

You can achieve it like this:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>

<script>
       window.jQuery || document.write('<script src="/path/to/your/jquery"><\/script>');
</script>

This should be in your page's <head> and any jQuery ready event handlers should be in the <body> to avoid errors (although it's not fool-proof!).

One more reason to not use Google-hosted jQuery is that in some countries, Google's domain name is banned.

Arsen Khachaturyan
  • 6,472
  • 4
  • 32
  • 36
Rony
  • 8,825
  • 2
  • 20
  • 22
  • Yes. As in Iran. "I'm not saying Google is flaky. There are cases where the Google copy is blocked (apparently in Iran, for instance)." – Nosredna Jun 18 '09 at 18:01
  • I assume you would want to write an alternate script tag in that document.write('') ? – jjxtra Jun 18 '09 at 18:02
  • that's a nice point I am not sure how to force these steps to behave in a synchronous manner – Rony Jun 18 '09 at 18:11
  • there must be some way in JavaScript to check the existence of a file and if it returns a 400 class status message then the local script can be downloaded, but I am not sure how that works it tried some examples but its not working for me – Rony Jun 18 '09 at 18:34
  • @Rony, see my edited question. You can use google.load to load jQuery off Google's site. That might help forcing the problem into being synchronous. – Nosredna Jun 18 '09 at 18:40
  • 2
    @Nosredna the problem with this in turn is that which file contains the google.load method, that is again hosted with google – Rony Jun 18 '09 at 18:56
  • I believe that using a self hosted version of jsapi will make some Google services unusable. For example, ClientLocation. – Ionuț G. Stan Jun 18 '09 at 19:16
  • In reference to making it synchronous: the fact that Google doesn't even attempt to address this issue in their typical jsapi include setup makes me think that it's not only extremely unlikely to be fixable, but also of minimal impact. (see http://code.google.com/apis/ajax/documentation/#GettingStarted - third code block) – Matchu Jun 22 '09 at 01:28
  • 35
    Aren't javascript downloads blocking (synchronous) already? Seems to me the double-copy issue would therefore not be a problem. – Matt Sherman Jun 22 '09 at 02:08
  • 68
    Javascript downloads should be synchronous already, as Matt Sherman said. Otherwise, many problems would occur if the page tried to execute an inline script that relied on a library that was only half downloaded, or a library extension was executed without the library fully downloaded and executed. That's also one reason why Yahoo YSlow recomends placing javascript at the end of pages; so that it doesn't block the downloading of other page elements (including styles and images). At the very least, the browser would have to delay execution to occur sequentially. – gapple Jun 24 '09 at 20:16
  • 43
    Small fix from a validator fanatic: The string '' is not allowed in JavaScript, because it could be misinterpreted as the end of the script tag (SGML short tag notation). Do '' instead. Cheers, – Boldewyn Jun 26 '09 at 08:13
  • 8
    This example will not work. 1) if Google ajax library is not available it'll have to time out first before failing. This may take a while. In my test of disconnecting my computer from the network it just tried and tried and tried and didn't timeout. 2) if (!jQuery) will throw an error because jQuery is not defined so Javascript doesn't know what to do with it. – RedWolves Jun 28 '09 at 13:42
  • 32
    To test if jQuery was loaded, (!window.jQuery) works fine, and is shorted then the typeof check. – Jörn Zaefferer Jul 26 '09 at 12:52
  • 2
    I think the expression is "fool-proof" but I could be making this up. – sova Oct 29 '10 at 21:31
  • 2
    Be aware that this solution could break your page for 30 seconds or longer as the browser waits for DNS or TCP to time out. – Matthew Jan 09 '11 at 04:53
  • 3
    short and robust: http://snipplr.com/view/41234/use-google-cdn-jquery-but-fallback-to-local-copy/ – mikkelbreum Jan 21 '11 at 14:36
  • 1
    @Boldewyn: Actually, the string `""` is allowed, but `" – Mathias Bynens Apr 05 '11 at 11:40
  • 3
    @Ryan: ` – Mathias Bynens Apr 05 '11 at 11:41
  • 2
    Well, instead of `""`, you could do `""`, which equally escapes it. – kevinji May 24 '11 at 18:56
  • There's no need to `unescape` the ` – kevinji Dec 06 '11 at 02:12
  • 2
    I edited the first src attribute to not include the scheme, so now the src is //ajax.googleapis.com/ajax/... This assures the reference works on both HTTP and HTTPS and is completely valid per http://stackoverflow.com/questions/550038/is-it-valid-to-replace-http-with-in-a-script-src-http – Cory House Dec 07 '11 at 20:21
  • 1
    you should place this just before

    and all jQuery code between this code and , so your site loads faster, don't place it in the head

    – Ruben Mar 21 '12 at 15:58
  • 2
    I prefer BenjaminRH's solution over this one. – Graeck Aug 15 '13 at 16:37
  • can someone explain to me why there is a `\/`? – Jayen Apr 25 '14 at 03:22
  • @kleinfreund, not really. the comments only talk about escapes and validation errors, not *why* it is an error or how it resolves it. *why* don't all the `/`'s need to be escaped? *why* just this one? also, adding such an explanation to the answer would help improve the answer, imho. – Jayen Jun 21 '14 at 22:23
  • 1
    @Jayen [this comment](http://stackoverflow.com/questions/1014203/best-way-to-use-googles-hosted-jquery-but-fall-back-to-my-hosted-library-on-go/1014251?noredirect=1#comment859232_1014251) and [this](http://stackoverflow.com/questions/1014203/best-way-to-use-googles-hosted-jquery-but-fall-back-to-my-hosted-library-on-go/1014251?noredirect=1#comment6311092_1014251). – kleinfreund Jun 22 '14 at 08:31
  • Also keep in mind that Google will then use this to track the websites that users go to. So if you are making a website that needs to be conscious of privacy, then hosting a couple of small files is a small price to pay for privacy. – Hans-Christoph Steiner Jul 13 '14 at 02:31
  • 1
    When will the local fallback load exactly? How long does it wait for Google jQuery CDN to finish? Will it load the fallback if the Google load is practically finished but delayed just a few miliseconds? (double jquery load = not good) – mowgli Aug 27 '14 at 18:36
  • For my case with GoogleChrome 44 the document.write thing doesn't work, I used the following one var script = document.createElement("script"); script.setAttribute('type', 'text/javascript'); script.setAttribute('src', '/path/to/your/jquery'); document.head.appendChild(script); – Angshu Guha Aug 10 '15 at 14:23
  • @AngshuGuha Everything works here with Chrome 51 and Firefox 47 @ Mac OS X. Maybe this is code-related to your specific case, good alternative though. :) – Dennis98 Jul 11 '16 at 12:11
  • http://stackoverflow.com/a/39246792/622813 – l2aelba Aug 31 '16 at 10:30
336

The easiest and cleanest way to do this by far:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="path/to/your/jquery"><\/script>')</script>
BenjaminRH
  • 11,269
  • 5
  • 45
  • 75
  • 1
    @jpp not for `XHTML 1.0` and `HTML 4.01` – BenjaminRH May 01 '13 at 15:18
  • 5
    People keep on asking me to remove the `type="text/javascript"` parts, so to people writing html for older browsers, note that you'll now have to add that in. – BenjaminRH May 28 '13 at 08:48
  • Could this be manipulated to check for the library from two hosts? For example, if [Google's](http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js) **or** [Microsoft's](http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.2.min.js) has already been downloaded, don't download? (While still providing a local fallback) – Trojan Jul 22 '13 at 22:37
  • 7
    @BenjaminRH: `type="text/javascript"` was unnecessary in older browsers too, since they all defaulted to Javascript. _Really_ older browsers looked at the `language` attribute; but even then, Javascript was the default if the attribute was missing. – Martijn Jul 26 '13 at 16:30
  • 1
    @Martijn But I liked the shiny validation badge :) – BenjaminRH Jul 26 '13 at 17:48
  • Is this possible with other .js libraries? – Daft Nov 20 '13 at 12:17
  • 3
    @Trojan Totally possible, just stack the calls. Note that at this point you're opening new connections hosts, so HTTP pipelining will probably be faster. ... ` ` – Tom McKenzie Mar 06 '14 at 22:16
  • Why not `window.$ || ...` instead of `window.jQuery || ...`? – Bob Stein Jul 14 '16 at 12:47
76

This seems to work for me:

<html>
<head>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
// has the google object loaded?
if (window.google && window.google.load) {
    google.load("jquery", "1.3.2");
} else {
    document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}
window.onload = function() {
    $('#test').css({'border':'2px solid #f00'});
};
</script>
</head>
<body>
    <p id="test">hello jQuery</p>
</body>
</html>

The way it works is to use the google object that calling http://www.google.com/jsapi loads onto the window object. If that object is not present, we are assuming that access to Google is failing. If that is the case, we load a local copy using document.write. (I'm using my own server in this case, please use your own for testing this).

I also test for the presence of window.google.load - I could also do a typeof check to see that things are objects or functions as appropriate. But I think this does the trick.

Here's just the loading logic, since code highlighting seems to fail since I posted the whole HTML page I was testing:

if (window.google && window.google.load) {
    google.load("jquery", "1.3.2");
} else {
    document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}

Though I must say, I'm not sure that if this is a concern for your site visitors you should be fiddling with the Google AJAX Libraries API at all.

Fun fact: I tried initially to use a try..catch block for this in various versions but could not find a combination that was as clean as this. I'd be interested to see other implementations of this idea, purely as an exercise.

Alex Tartan
  • 6,465
  • 10
  • 33
  • 41
artlung
  • 30,810
  • 16
  • 66
  • 118
  • 1
    What is the advantage of using google.load in this situation, rather than loading ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js directly, like Rony suggested? I guess loading it directly catches issues with removed libraries as well (what if Google stops serving JQuery 1.3.2). Furthermore, Rony's version notices network problems AFTER www.google.com/jsapi has been fetched, especially when jsapi has been loaded from cache? One might need to use the google.load callback to be sure (or maybe there's some return value to include the google.load in the if(..)). – Arjan Jun 27 '09 at 08:32
  • If one is testing for the presence of Google.com, one could make a network call, or one could check for the presence of the "gatekeeper" object. What I'm doing is checking for the google object and its "load" function. If both of those fail, no google, and I need the local version. Rony's version actually ignores the www.google.com/jsapi URL entirely, so I'm not sure why you indicate that it will have been fetched. – artlung Jun 27 '09 at 15:26
  • In the end, all that's required is that the jquery library is loaded. Any Google library is not a requirement. In Rony's answer, one knows for sure if loading from Google (or the cache) succeeded. But in your check for "if (window.google && window.google.load)", the jquery library is still not loaded. The actual loading of the jquery library is not validated? – Arjan Jun 27 '09 at 18:45
  • ah, I see how I caused the confusion. "Rony's version notices network problems AFTER www.google.com/jsapi has been fetched" should better read: "Your version does not notice network problems AFTER www.google.com/jsapi has been fetched". – Arjan Jun 27 '09 at 18:47
  • 2
    We've recently switched to using Google as our jQuery host; if we get any bug reports from blocked users, I'll be using a variant of your answer to refactor our client code. Good answer! – Jarrod Dixon Jul 26 '09 at 07:45
31

If you have modernizr.js embedded on your site, you can use the built-in yepnope.js to load your scripts asynchronously - among others jQuery (with fallback).

Modernizr.load([{
    load : '//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'
},{
    test : window.jQuery,
    nope : 'path/to/local/jquery-1.7.2.min.js',
    both : ['myscript.js', 'another-script.js'],
    complete : function () {
        MyApp.init();
    }
}]);

This loads jQuery from the Google-cdn. Afterwards it's checked, if jQuery was loaded successfully. If not ("nope"), the local version is loaded. Also your personal scripts are loaded - the "both" indicates, that the load-process is iniated independently from the result of the test.

When all load-processes are complete, a function is executed, in the case 'MyApp.init'.

I personally prefer this way of asynchronous script loading. And as I rely on the feature-tests provided by modernizr when building a site, I have it embedded on the site anyway. So there's actually no overhead.

K.Dᴀᴠɪs
  • 9,384
  • 11
  • 31
  • 39
Emanuel Kluge
  • 697
  • 5
  • 9
  • 2
    I think you're missing the point of the question - how would you go about loading the moernizr script from a CDN? – George Filippakos Apr 26 '13 at 07:06
  • 2
    I can't recommend loading Modernizr from a CDN. One should rather get the smallest custom build from modernizr.com. – Emanuel Kluge Jun 12 '14 at 10:45
  • 2
    So this option gets +16, compared to the 500/200+ the other options are getting. But this sounds quite good. Is it just not popular due to relying on Modernizer? I happen to using Modernizer in our site anyway, so if this is better than the other answers, can someone let me know? Im quite new to JQuery, so clarification is appreciated. – redfox05 Oct 10 '14 at 09:02
  • 2
    This was a really good option in the time of the answer, but as of 2015, `yepnope.js` is deprecated . see http://stackoverflow.com/questions/33986561/modernizr-load-deprecated-yepnope-js-deprecated-now-what – Obmerk Kronen Jan 01 '17 at 00:01
  • Modernizr was created to solve problems just like this question. +1 – Carlos Quijano Jun 18 '17 at 21:14
21

There are some great solutions here, but I'll like to take it one step further regarding the local file.

In a scenario when Google does fail, it should load a local source but maybe a physical file on the server isn't necessarily the best option. I bring this up because I'm currently implementing the same solution, only I want to fall back to a local file that gets generated by a data source.

My reasons for this is that I want to have some piece of mind when it comes to keeping track of what I load from Google vs. what I have on the local server. If I want to change versions, I'll want to keep my local copy synced with what I'm trying to load from Google. In an environment where there are many developers, I think the best approach would be to automate this process so that all one would have to do is change a version number in a configuration file.

Here's my proposed solution that should work in theory:

  • In an application configuration file, I'll store 3 things: absolute URL for the library, the URL for the JavaScript API, and the version number
  • Write a class which gets the file contents of the library itself (gets the URL from app config), stores it in my datasource with the name and version number
  • Write a handler which pulls my local file out of the db and caches the file until the version number changes.
  • If it does change (in my app config), my class will pull the file contents based on the version number, save it as a new record in my datasource, then the handler will kick in and serve up the new version.

In theory, if my code is written properly, all I would need to do is change the version number in my app config then viola! You have a fallback solution which is automated, and you don't have to maintain physical files on your server.

What does everyone think? Maybe this is overkill, but it could be an elegant method of maintaining your AJAX libraries.

Acorn

K.Dᴀᴠɪs
  • 9,384
  • 11
  • 31
  • 39
Acorn
  • 867
  • 7
  • 13
  • If you're doing all that work *just* for jQuery, then I'd say it is overkill. However, if you already have some of those components in place for other pieces of your app (e.g. if you already load scripts from a DB) then it looks pretty nice. – Michael Haren Jan 19 '10 at 13:28
  • 1
    +1 for being thorough and novel, though I'm not convinced the benefit justifies the dev time and complexity. – Cory House Dec 07 '11 at 20:24
20
if (typeof jQuery == 'undefined') {
// or if ( ! window.jQuery)
// or if ( ! 'jQuery' in window)
// or if ( ! window.hasOwnProperty('jQuery'))    

  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = '/libs/jquery.js';

  var scriptHook = document.getElementsByTagName('script')[0];
  scriptHook.parentNode.insertBefore(script, scriptHook);

}

After you attempt to include Google's copy from the CDN.

In HTML5, you don't need to set the type attribute.

You can also use...

window.jQuery || document.write('<script src="/libs/jquery.js"><\/script>');
alex
  • 438,662
  • 188
  • 837
  • 957
  • 2
    +1 looks more cleaner. there is a minor typo at top which i cannot clear since its ery minor two closing brackets after 'undefined' – naveen Feb 27 '11 at 05:48
  • 1
    First option avoids the Chrome warning `[Violation] Avoid using document.write().` – Bob Stein May 29 '17 at 16:30
  • First option, unfortunately, does not appear to load [synchronously](https://stackoverflow.com/q/3248384/673991). Second option [does](https://stackoverflow.com/a/3292763/673991). – Bob Stein May 29 '17 at 17:40
10

You might want to use your local file as a last resort.

Seems as of now jQuery's own CDN does not support https. If it did you then might want to load from there first.

So here's the sequence: Google CDN => Microsoft CDN => Your local copy.

<!-- load jQuery from Google's CDN -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> 
<!-- fallback to Microsoft's Ajax CDN -->
<script> window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.min.js">\x3C/script>')</script> 
<!-- fallback to local file -->
<script> window.jQuery || document.write('<script src="Assets/jquery-1.8.3.min.js">\x3C/script>')</script> 
Edward Olamisan
  • 1,192
  • 1
  • 14
  • 22
  • Is there really a need for more than one fallback? if both are offline user will be waiting for over a minute before seeing your site – George Filippakos Apr 26 '13 at 07:09
  • 1
    It doesn't take 1 minute for a script to fail to load, does it. – Edward Olamisan Apr 26 '13 at 17:12
  • @geo1701 and Edward, There is really no need for a third. Even one fallback has yet has yet to be proven reliable. If the Google API is down, I have not yet seen any guarantees that the first attempt will fail at all. I experienced a case scenario where a CDN never failed to load, holding the page from ever rendering, as mentioned here: http://www.stevesouders.com/blog/2013/03/18/http-archive-jquery/#comment-52712 – hexalys Apr 30 '13 at 00:25
6

Conditionally load latest/legacy jQuery version and fallback:

<!--[if lt IE 9]>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script>window.jQuery || document.write('<script src="/public/vendor/jquery-legacy/dist/jquery.min.js">\x3C/script>')</script>
<![endif]-->
<!--[if gte IE 9]><!-->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script>window.jQuery || document.write('<script src="/public/vendor/jquery/dist/jquery.min.js">\x3C/script>')</script>
<!--<![endif]-->
neiker
  • 8,568
  • 3
  • 27
  • 31
5

Because of the Google's banning problem I prefer to use Microsoft's cdn http://www.asp.net/ajaxlibrary/cdn.ashx

K.Dᴀᴠɪs
  • 9,384
  • 11
  • 31
  • 39
Serdar
  • 1,270
  • 1
  • 15
  • 41
5
  • Step 1: Did jQuery fail to load? (check jQuery variable)

How to check a not-defined variable in JavaScript

  • Step 2: Dynamically import (the backup) javascript file

How do I include a JavaScript file in another JavaScript file?

Community
  • 1
  • 1
ninjagecko
  • 77,349
  • 22
  • 129
  • 137
4

UPDATE:
This answer turned out to be wrong. Please see the comments for the real explanation.


Most of you question has been answered, but as for the final part:

What would be the danger of both copies coming through?

None really. You'd waste bandwidth, might add some milliseconds downloading a second useless copy, but there's not actual harm if they both come through. You should, of course, avoid this using the techniques mentioned above.

WhyNotHugo
  • 8,246
  • 5
  • 56
  • 63
  • 5
    Actually, loading jQuery twice can cause lots of problems, according to [this question](http://stackoverflow.com/questions/2731897/loading-jquery-twice-causes-an-error). – ShadowCat7 Sep 06 '13 at 20:10
  • why don't you test it yourself and manually load the jquery library twice. then the answer will be revealed. – luke_mclachlan Jun 29 '15 at 13:03
  • Why exactly is it so wrong? @ShadowCat7 can you be more specific about the problems it causes? The only problem I see explicitly identified in the question you linked is "clearing all previously loaded plugins." But that should not apply to loading the same jQuery file twice back-to-back, right? I ask because the other solutions here to local fallback are so convoluted, and document.write is maligned as [evil](https://stackoverflow.com/a/27531116/673991) in some [places](https://www.reddit.com/r/webdev/comments/52dn3x/chrome_is_intervening_against_documentwrite/?st=j3br3bhp&sh=9bb65589). – Bob Stein May 30 '17 at 16:07
4

For those people using ASP.NET MVC 5, add this code in your BundleConfig.cs to enable the CDN for jquery:

bundles.UseCdn = true;
Bundle jqueryBundle = new ScriptBundle("~/bundles/jquery", "//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js").Include("~/Scripts/jquery-{version}.js");
jqueryBundle.CdnFallbackExpression = "window.jQuery";
bundles.Add(jqueryBundle);
Muhammad Rehan Saeed
  • 28,236
  • 27
  • 169
  • 261
4

Here is a great explaination on this!

Also implements loading delays and timeouts!

http://happyworm.com/blog/2010/01/28/a-simple-and-robust-cdn-failover-for-jquery-14-in-one-line/

Stuart.Sklinar
  • 3,480
  • 4
  • 30
  • 77
  • Link-only answers are not useful and are considered low quality. Consider copying the relevant bits into your answer, with attribution of course to the source. – random_user_name May 07 '18 at 18:37
  • @cale_b Are you kidding? This answer is over 7 years old, so such a comment is unwarranted. – Stuart.Sklinar May 08 '18 at 07:47
  • Yeah, it's an old answer. Though their suggestion is valid. Answers which are merely links elsewhere are candidates for deletion. Further reader: https://meta.stackoverflow.com/q/8259 – Rob May 08 '18 at 10:13
  • I totally agree, I'd moderate with suggestions myself - but it's pointless saying it 7 years later. It should have been moderated like that 7 years ago, not 7 years later. – Stuart.Sklinar May 08 '18 at 11:20
  • 1
    @Stuart.Sklinar - if I had seen it 7 years ago, I would have :) I found myself here doing some research, and seeing this for the first time. Sorry to frustrate you - I think our jobs on SO are to be stewards of the site, which sometimes means commenting, editing, or improving old questions or answers... – random_user_name May 08 '18 at 13:11
2

I consider that should escape the last < to \x3C in string. When the browser sees , it considers this to be the end of the script block (since the HTML parser has no idea about JavaScript, it can't distinguish between something that just appears in a string, and something that's actually meant to end the script element). So appearing literally in JavaScript that's inside an HTML page will (in the best case) cause errors, and (in the worst case) be a huge security hole.

<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery-2.0.0.min.js">\x3C/script>')</script>
Josh Habdas
  • 6,370
  • 2
  • 53
  • 55
JKhuang
  • 1,433
  • 1
  • 12
  • 14
2

I made a Gist that should dynamically load jQuery if it isn't already loaded, and if the source fails, it proceeds onto fallbacks (stitched together from many answers): https://gist.github.com/tigerhawkvok/9673154

Please note I plan to keep the Gist updated but not this answer, for what it's worth!

/* See https://gist.github.com/tigerhawkvok/9673154 for the latest version */
function cascadeJQLoad(i) { // Use alternate CDNs where appropriate to load jQuery
    if (typeof(i) != "number") i = 0;
    // the actual paths to your jQuery CDNs
    var jq_paths = [
        "ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js",
        "ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.0.min.js"
    ];
    // Paths to your libraries that require jQuery
    var dependent_libraries = [
        "js/c.js"
    ];
    if (window.jQuery === undefined && i < jq_paths.length) {
        i++;
        loadJQ(jq_paths[i], i, dependent_libraries);
    }
    if (window.jQuery === undefined && i == jq_paths.length) {
        // jQuery failed to load
        // Insert your handler here
    }
}

/***
 * You shouldn't have to modify anything below here
 ***/

function loadJQ(jq_path, i, libs) { //load jQuery if it isn't already
    if (typeof(jq_path) == "undefined") return false;
    if (typeof(i) != "number") i = 1;
    var loadNextJQ = function() {
        var src = 'https:' == location.protocol ? 'https' : 'http';
        var script_url = src + '://' + jq_path;
        loadJS(script_url, function() {
            if (window.jQuery === undefined) cascadeJQLoad(i);
        });
    }
    window.onload = function() {
        if (window.jQuery === undefined) loadNextJQ();
        else {
            // Load libraries that rely on jQuery
            if (typeof(libs) == "object") {
                $.each(libs, function() {
                    loadJS(this.toString());
                });
            }
        }
    }
    if (i > 0) loadNextJQ();
}

function loadJS(src, callback) {
    var s = document.createElement('script');
    s.src = src;
    s.async = true;
    s.onreadystatechange = s.onload = function() {
        var state = s.readyState;
        try {
            if (!callback.done && (!state || /loaded|complete/.test(state))) {
                callback.done = true;
                callback();
            }
        } catch (e) {
            // do nothing, no callback function passed
        }
    };
    s.onerror = function() {
        try {
            if (!callback.done) {
                callback.done = true;
                callback();
            }
        } catch (e) {
            // do nothing, no callback function passed
        }
    }
    document.getElementsByTagName('head')[0].appendChild(s);
}

/*
 * The part that actually calls above
 */

if (window.readyState) { //older microsoft browsers
    window.onreadystatechange = function() {
        if (this.readyState == 'complete' || this.readyState == 'loaded') {
            cascadeJQLoad();
        }
    }
} else { //modern browsers
    cascadeJQLoad();
}
Philip Kahn
  • 490
  • 4
  • 15
2

Google Hosted jQuery

  • If you care about older browsers, primarily versions of IE prior to IE9, this is the most widely compatible jQuery version
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  • If you don’t care about oldIE, this one is smaller and faster:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

Backup/Fallback Plan!

  • Either way, you should use a fallback to local just in case the Google CDN fails (unlikely) or is blocked in a location that your users access your site from (slightly more likely), like Iran or sometimes China.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>if (!window.jQuery) { document.write('<script src="/path/to/your/jquery"><\/script>'); }
</script>

Reference: http://websitespeedoptimizations.com/ContentDeliveryNetworkPost.aspx

Ryan
  • 791
  • 5
  • 10
2
if (typeof jQuery == 'undefined')) { ...

Or

if(!window.jQuery){

Will not works if cdn version not loaded, because browser will run through this condition and during it still downloading the rest of javascripts which needs jQuery and it returns error. Solution was to load scripts through that condition.

    <script src="http://WRONGPATH.code.jquery.com/jquery-1.4.2.min.js" type="text/javascript"></script><!--  WRONGPATH for test-->
  <script type="text/javascript">
  function loadCDN_or_local(){
    if(!window.jQuery){//jQuery not loaded, take a local copy of jQuery and then my scripts
      var scripts=['local_copy_jquery.js','my_javascripts.js'];
      for(var i=0;i<scripts.length;i++){
      scri=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
      scri.type='text/javascript';
      scri.src=scripts[i];
    }
  }
  else{// jQuery loaded can load my scripts
    var s=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
    s.type='text/javascript';
    s.src='my_javascripts.js';
  }
  }
  window.onload=function(){loadCDN_or_local();};
  </script>
K.Dᴀᴠɪs
  • 9,384
  • 11
  • 31
  • 39
  • I found one problem in testing scripts in Google Chrome - caching. So for local testing just replace src in else section with something like s.src='my_javascripts.js'+'?'+Math.floor(Math.random()*10001); – Mirek Komárek Feb 27 '11 at 09:42
  • *[Alex's answer](http://stackoverflow.com/questions/1014203/best-way-to-use-googles-hosted-jquery-but-fall-back-to-my-hosted-library-on-goo/4825258#4825258) will not work if cdn version not loaded, because browser will run through this condition and during it still downloading the rest of javascripts which needs jquery and it returns error* -> [JavaScript files being downloaded will block the next piece of code from being ran so it's not an issue](http://www.yuiblog.com/blog/2008/07/22/non-blocking-scripts/). – alex Apr 19 '11 at 01:16
2

Almost all public CDNs are pretty reliably. However, if you are worried about blocked google domain, then you can simply fallback to an alternative jQuery CDN. However, in such a case, you may prefer to do it opposite way and use some other CDN as your preferred option and fallback to Google CDN to avoid failed requests and waiting time:

<script src="https://pagecdn.io/lib/jquery/3.2.1/jquery.min.js"></script>
<script>
   window.jQuery || document.write('<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"><\/script>');
</script>
Hamid Sarfraz
  • 1,066
  • 1
  • 13
  • 32
1

Although writing document.write("<script></script>") seems easier for jQuery backoff, Chrome gives validation error on that case. So I prefer breaking "script" word. So it becomes safer like above.

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<script>if (typeof jQuery === "undefined") {
   window.jqFallback = true;
   document.write("<scr"+"ipt src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js'></scr"+"ipt>");
} </script>

For long term issues, it would be better to log JQuery fallbacks. In the code above, if first CDN is not available JQuery is loaded from another CDN. But you could want to know that erroneous CDN and remove it permanently. (this case is very exceptional case) Also it is better to log fallback issues. So you can send erroneous cases with AJAX. Because of JQuery isn't defined, you should use vanilla javascript for AJAX request.

<script type="text/javascript">
    if (typeof jQuery === 'undefined' || window.jqFallback == true) {
        // XMLHttpRequest for IE7+, Firefox, Chrome, Opera, Safari
        // ActiveXObject for IE6, IE5
        var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        var url = window.jqFallback == true ? "/yourUrl/" : "/yourUrl2/";
        xmlhttp.open("POST", url, true);
        xmlhttp.send();
    }
</script>
trante
  • 30,976
  • 45
  • 174
  • 260
1

The inability to load the resource from an external data store beyond your control is difficult. Looking for missing functions is totally fallacious as a means to avoid suffering a timeout, as described herein: http://www.tech-101.com/support/topic/4499-issues-using-a-cdn/

jobeard
  • 145
  • 6
1

Using Razor syntax in ASP.NET, this code provides fallback support and works with a virtual root:

@{var jQueryPath = Url.Content("~/Scripts/jquery-1.7.1.min.js");}
<script type="text/javascript">
    if (typeof jQuery == 'undefined')
        document.write(unescape("%3Cscript src='@jQueryPath' type='text/javascript'%3E%3C/script%3E"));
</script>

Or make a helper (helper overview):

@helper CdnScript(string script, string cdnPath, string test) {
    @Html.Raw("<script src=\"http://ajax.aspnetcdn.com/" + cdnPath + "/" + script + "\" type=\"text/javascript\"></script>" +
        "<script type=\"text/javascript\">" + test + " || document.write(unescape(\"%3Cscript src='" + Url.Content("~/Scripts/" + script) + "' type='text/javascript'%3E%3C/script%3E\"));</script>")
}

and use it like this:

@CdnScript("jquery-1.7.1.min.js", "ajax/jQuery", "window.jQuery")
@CdnScript("jquery.validate.min.js", "ajax/jquery.validate/1.9", "jQuery.fn.validate")
Edward Brey
  • 35,877
  • 14
  • 173
  • 224
  • I've never heart about Razor, but it looks like an obfuscator, except for that it makes the code longer rather than shorter (it's twice as long as [this](http://stackoverflow.com/a/9400432/581205). – maaartinus May 15 '14 at 23:37
  • @maaartinus: That's not an apples-to-apples comparison. BenjaminRH's answer, which you refer to, is for a single CDN-hosted script. With the `CdnScript` helper, you need only one line of code *per script*. The more scripts you have, the bigger the payoff. – Edward Brey May 17 '14 at 01:50
  • Sure... it was just a rant. However, I guess that's not the optimal way. If anything fails, I'd ignore CDN completely and switch to the fallback for all scripts. I'm not sure if this is doable as I don't know how the loading exactly works. – maaartinus May 17 '14 at 05:54
  • @maaartinus: Since each CDN script load can fail independently, you have to check each load separately. There is no reliable method of a single CDN check followed by loading all scripts from CDN vs. locally. – Edward Brey May 17 '14 at 11:37
  • The case that worries me is a failure of the CDN site leading to wait times for many loads. So I'd like to have something like `try { for (Script s : ...) cdnLoad(s); } catch (...) { for (Script s : ...) ownLoad(s); }`. Translating this into a bunch of `if`s could be a nightmare. – maaartinus May 17 '14 at 11:48
  • @maaartinus: That approach works if scripts are designed to be multiply loaded. Otherwise, if you have scripts A, B, and C, and A and B load from the CDN, but C fails, you would get a load sequence of {A, B, A, B, C}, with the first two loaded from CDN and the last three loaded from the web server. It seems like you need a g`tryCdn` variable initially set to true. Then each `cdnLoad` method would try the CDN first only if `tryCdn` is still true, and set it to false if a CDN load fails. – Edward Brey May 17 '14 at 19:05
  • This makes sense! Additionally, after a couple of failures, which my server sees, I would let it avoid CDN for a while. – maaartinus May 17 '14 at 19:14
0

Yet another fallback that replaces ajax.googleapis.com with cdnjs.cloudflare.com:

(function (doc, $)
{
    'use strict';

    if (typeof $ === 'undefined')
    {
        var script = doc.querySelector('script[src*="jquery.min.js"]'),
            src = script.src.replace('ajax.googleapis.com', 'cdnjs.cloudflare.com');

        script.parentNode.removeChild(script);
        doc.write('<script src="' + src + '"></script>');
    }
})(document, window.jQuery || window.Zepto);
  • You can stick to a jQuery version by specifying it in the string
  • Perfect for Asset Management that doesn't work with HTML snips
  • Tested in the wild - works perfect for users from China
redaxmedia
  • 962
  • 1
  • 11
  • 23
  • Could you elaborate on the following statement: "You don't have to care about the jQuery version"? – Josh Habdas Jun 11 '17 at 05:51
  • The version is part of the URL that is not goint to be touched by this approach... jquery/3.x.x/jquery.min.js – redaxmedia Sep 18 '17 at 08:19
  • 1
    Does that have the potential to cause breakage when jQuery revs to version 4 and introduces backwards incompatible changes? – Josh Habdas Sep 18 '17 at 11:41
  • -1 because that will cause breakage if jQuery introduces breaking changes that your scripts won't support yet unless the version is specified. – Lookaji Sep 22 '18 at 22:44
  • @lookaji I think you don't understand the fallback. It does replace the domain where it is hosted and NOT touch the filename / version at all. – redaxmedia Sep 24 '18 at 12:07
  • Sorry but I did not read your comment about keeping the version part untouched. Let me edit it so I can withdraw the -1 as I find (and apparently not only me judging by others too) your answer it a bit confusing. The fallback mechanism is to me obviously clear, btw :) – Lookaji Sep 25 '18 at 13:36
0

You can use code like:

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>window.jQuery || document.write('<script type="text/javascript" src="./scripts/jquery.min.js">\x3C/script>')</script>

But also there are libraries you can use to setup several possible fallbacks for your scripts and optimize the loading process:

  • basket.js
  • RequireJS
  • yepnope

Examples:

basket.js I think the best variant for now. Will cach your script in the localStorage, that will speed up next loadings. The simplest call:

basket.require({ url: '/path/to/jquery.js' });

This will return a promise and you can do next call on error, or load dependencies on success:

basket
    .require({ url: '/path/to/jquery.js' })
    .then(function () {
        // Success
    }, function (error) {
        // There was an error fetching the script
        // Try to load jquery from the next cdn
    });

RequireJS

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

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

yepnope

yepnope([{
  load: 'http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js',
  complete: function () {
    if (!window.jQuery) {
      yepnope('js/jquery-2.0.0.min.js');
    }
  }
}]);