Friday, May 25, 2012

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


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/


Source: Tips4all

10 comments:

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

    But this is how you achieve it:

    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
    <script type="text/javascript">
    if (typeof jQuery == 'undefined')
    {
    document.write(unescape("%3Cscript src='/path/to/your/jquery' type='text/javascript'%3E%3C/script%3E"));
    }
    </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 full-proof!).

    ReplyDelete
  2. 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.

    ReplyDelete
  3. 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 js 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

    ReplyDelete
  4. The easiest and cleanest way to do this by far:

    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script type="text/javascript">window.jQuery || document.write('<script type="text/javascript" src="path/to/your/jquery"><\/script>')</script>

    ReplyDelete
  5. 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>');

    ReplyDelete
  6. Because of the google's banning problem I prefer to use microsoft's cdn
    http://www.asp.net/ajaxlibrary/cdn.ashx

    ReplyDelete
  7. 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


    Include JavaScript file inside JavaScript file?

    ReplyDelete
  8. 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/

    ReplyDelete
  9. 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>

    ReplyDelete
  10. 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")

    ReplyDelete