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

Viewed 164606

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/

23 Answers

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.

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.

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

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>');

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>

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');
    }
  }
}]);

You should never need to use a personal copy. Here is my full belt and braces script.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8"/>
        <title>jQuery</title>
        <!-- EMBED JQUERY USING GOOGLE APIs -->
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

        <!-- IF THAT FAILS EMBED JQUERY USING CLOUDFLARE CDN -->
        <script type="text/javascript">
            window.jQuery || document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"><\/script>');
        </script>

        <!-- IF THAT FAILS ************* HOW??? *********** EMBED JQUERY FROM MICROSOFT -->
        <script type="text/javascript">
            window.jQuery || document.write('<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.6.0.min.js"><\/script>');
        </script>

        <!-- IF THAT FAILS ************* WOW!!! *********** EMBED JQUERY FROM JQUERY.COM -->
        <script type="text/javascript">
            window.jQuery || document.write('<script src="https://code.jquery.com/jquery-3.6.0.min.js"><\/script>');
        </script>

        <!-- IF THAT FAILS ************* LET'S STOP!!! *********** EMBED JQUERY FROM PERSONAL COPY -->
        <script type="text/javascript">
            window.jQuery || document.write('<script src="jquery.min.js"><\/script>');
        </script>
    </head>
    <body>


    </body>
</html>
Related