Best way to use Google's hosted jQuery, but fall back to my hosted library on Google fail

162,986

Solution 1

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.

Solution 2

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>

Solution 3

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.

Solution 4

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.

Solution 5

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

Share:
162,986
Nosredna
Author by

Nosredna

I can clap with one hand. I got married in Mick Fleetwood's pants. Gregg Keizer once had me edit an Orson Scott Card column. A game Sega published was based on a dream I had. I have an officially licensed Star Trek tunic from before the first film! Anyone know what that's worth? Senior Software Engineer, Playdom

Updated on November 27, 2021

Comments

  • Nosredna
    Nosredna over 2 years

    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/

  • Gaurang Shah
    Gaurang Shah about 15 years
    Aren't javascript downloads blocking (synchronous) already? Seems to me the double-copy issue would therefore not be a problem.
  • gapple
    gapple about 15 years
    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.
  • Boldewyn
    Boldewyn about 15 years
    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 '<'+'/script>' instead. Cheers,
  • Arjan
    Arjan about 15 years
    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(..)).
  • artlung
    artlung about 15 years
    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.
  • Arjan
    Arjan about 15 years
    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
    Arjan about 15 years
    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".
  • Jarrod Dixon
    Jarrod Dixon almost 15 years
    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!
  • dbslone
    dbslone almost 15 years
    To test if jQuery was loaded, (!window.jQuery) works fine, and is shorted then the typeof check.
  • Mirek Komárek
    Mirek Komárek over 13 years
    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‌​);
  • user3167101
    user3167101 about 13 years
    Alex's answer 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.
  • ShadowCat7
    ShadowCat7 almost 11 years
    Actually, loading jQuery twice can cause lots of problems, according to this question.
  • maaartinus
    maaartinus about 10 years
    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.
  • Edward Brey
    Edward Brey about 10 years
    @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.
  • maaartinus
    maaartinus about 10 years
    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.
  • Edward Brey
    Edward Brey about 10 years
    @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.
  • maaartinus
    maaartinus about 10 years
    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 ifs could be a nightmare.
  • Edward Brey
    Edward Brey about 10 years
    @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 gtryCdn 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.
  • maaartinus
    maaartinus about 10 years
    This makes sense! Additionally, after a couple of failures, which my server sees, I would let it avoid CDN for a while.
  • luke_mclachlan
    luke_mclachlan almost 9 years
    why don't you test it yourself and manually load the jquery library twice. then the answer will be revealed.
  • Bob Stein
    Bob Stein about 7 years
    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 in some places.
  • vhs
    vhs about 7 years
    Note loading scripts over insecure protocols opens an XSS attack vector.
  • vhs
    vhs about 7 years
    Could you elaborate on the following statement: "You don't have to care about the jQuery version"?
  • Henry Ruhs
    Henry Ruhs almost 7 years
    The version is part of the URL that is not goint to be touched by this approach... jquery/3.x.x/jquery.min.js
  • vhs
    vhs almost 7 years
    Does that have the potential to cause breakage when jQuery revs to version 4 and introduces backwards incompatible changes?
  • Lookaji
    Lookaji almost 6 years
    -1 because that will cause breakage if jQuery introduces breaking changes that your scripts won't support yet unless the version is specified.
  • Henry Ruhs
    Henry Ruhs almost 6 years
    @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.
  • Lookaji
    Lookaji almost 6 years
    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 :)
  • Hooman Bahreini
    Hooman Bahreini about 3 years
    What should we do if the CDN has integrity and crossorigin attributes, such as this one: <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUq‌​U=" crossorigin="anonymous"></script>?
  • marko-36
    marko-36 over 2 years
    The link above is broken.