views:

422

answers:

4

I'd like to pull in the jquery library from my javscript include.

Does this work 100% of the time? Is there a better way?

(function() {

var loadJquery = function (cb) {
    var addLibs = function () {
        if (typeof(document.body) == "undefined" || document.body === null) {
            setTimeout(addLibs, 100);
            return;
        }

        var node = document.createElement("script");
        node.src = "http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js";
        document.body.appendChild(node);
        checkLib();
    };

    var checkLib = function () {  
        if (typeof($) == "undefined" || $("*") === null) {
            setTimeout(checkLib, 100);
            return;
        }
        cb($.noConflict());
    }
    addLibs();
}

loadJquery(function($){
  // Do stuff with $
  $(document.body).css("background", "black");
});

})();

Change the node.src and $.noConflict to YUI if you want YUI3, or YAHOO if you're YUI2, etc.

+3  A: 

There's only one issue I can think of with this approach (and this has come up twice in questions on SO in the last month).

The issue is that jQuery as of 1.3.2 cannot detect if the document is loaded if it is itself included after the document has loaded (i.e. dynamic <script> inclusion as you are doing, bookmarklet, etc) in non-IE browsers. In such cases the function passed to .ready() will never be called.

Here is the bug report for the issue: http://dev.jquery.com/ticket/4196 Nothing has happened with it for the last 8 months.

Now, the way you have designed loadJquery as I see it should effectively replace $(document).ready() so you may not need to worry. But any code that is written in the $(document).ready() function-passing style will not execute.

Crescent Fresh
Huh, that is really good to know. I was having this problem awhile back and had no clue why it was happening.
Bob Aman
Instead of relying on jQuery to detect that it is loaded, can't you include your own <script> tag right after the jQuery include, and invoke your "ready" handler there?
Ates Goral
@Ates Goral: I don't believe so, as the order in which dynamically created `<script>` tags are executed is not deterministic. IE will execute them in the order they finish downloading in (in this case the latter code wins everytime), whereas Firefox executes them in the order they are attached in the DOM. See http://blogs.msdn.com/kristoffer/archive/2006/12/22/loading-javascript-files-in-parallel.aspx for a more indepth explanation of IE's behavior.
Crescent Fresh
Ah! I didn't know that the order wasn't deterministic. Then that's not good news for JSONP error handling implementations that rely on an additional <script> tag being executed after the JSONP include.
Ates Goral
@Ates Goral: do you have an example library that does such a thing? Curious...
Crescent Fresh
I remember seeing it somewhere, but I wasn't able to locate it :(
Ates Goral
A: 

If you're looking for a standard way, instead of coming up with your own implementation, you could perhaps consider:

  • Google's google.load() for loading popular libraries
  • The CommonJS SecurableModules - see Kris Kowal's Chiron for a browser implementation.
Ates Goral
The google.load() will make me have to have another pre-include. I want just one include. I'm not really sure about CommonJS, can you point me at where they pull in another library from raw javascript?
Paul Tarjan
I'm not sure I understand what you mean by "from raw JS", but Chiron or any other CommonJS implementations out there could be used to see a reference implementation. Sorry, I don't have a more specific place to point you to.
Ates Goral
A: 

From posting on our internal mailing lists at work, I was pointed to this:

There are many cross-browser edge cases involved in dynamically injecting scripts. If rolling the YUI 3 seed file into your script isn't an option and if you can't depend on the property to already have the YUI 3 seed loaded, I'd recommend using LazyLoad (shameless plug) to bootstrap your script: http://github.com/rgrove/lazyload/

You can merge lazyload.js into your script, then use something like this to load the YUI 3 seed if it's not already on the page:

(function () {
   function init() {
     YUI().use('node', function (Y) {
       // ... do my stuff ...
     });
   }

   if (YUI) {
     init();
   } else {
     LazyLoad.js('http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js',
init);
   }
})();
Paul Tarjan
A: 

See my answer here for a reliable asynchronous require() function: http://stackoverflow.com/questions/950087/include-javascript-file-inside-javascript-file

nornagon