4

I don't think this can be done, though I know requirejs can do something similar, and I was wondering if this might be possible outside of require.

I'm writing a piece of SaaS JavaScript code that will most likely need jQuery to run cross browser. Some sites have it, some don't, but I don't want to stick jQuery on the window object if they aren't using it. Though I could individually create alternatives for everything I use jQuery for... I was wondering if there is a way I can load jQuery only for use inside my closure. Is this possible?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shane
  • 4,921
  • 5
  • 37
  • 53
  • possible duplicate of [How to encapsulate jQuery?](http://stackoverflow.com/questions/7937507/how-to-encapsulate-jquery) – Ricardo Souza Oct 08 '13 at 19:25
  • Fair point, didn't see this when I posted originally. If there are no objections, I'm going to leave this question since the wording will probably catch different kinds of searches as I wasn't able to find the original post before making this one. – Shane Oct 08 '13 at 20:04

3 Answers3

5

When you load the standard release of jQuery, by default jQuery assigns itself to the global names jQuery and $. If you load your version of jQuery before your closure file within the page, then within your closure do

var $ = jQuery.noConflict(true);

It will undo whatever modifications your version of jQuery did to the global object.

It is worth noting, however, that this produces a race condition. If any events or timeouts fire between the time that your jQuery file loads and your closure calls jQuery.noConflict(true) they will accidentally use the version of jQuery that you loaded.

An alternative is to edit the jQuery file that you are using to include a call to jQuery.noConflict(true) at the bottom of the jQuery file. You can assign the result to an arbitrary name that no one else would ever think to use, like "abcRandomNumberHere". Then you can retrieve your instance of jQuery from that global name within your closure assigning it to a local variable named jQuery and/or $ and delete the global reference at that time.

// Your jQuery file on your server
// ...
// End of file:
someRandomGlobalName = jQuery.noConflict(true);

// The file that contains your closure:
(function() {
    var $, jQuery;
    $ = jQuery = someRandomGlobalName;
    delete window.someRandomGlobalName;

    // Your code that uses jQuery here
} ());

This is a rough approximation of what RequireJS does to keep jQuery out of the global namespace. Recent versions of jQuery have built-in code that detects the existence of a script loader like RequireJS and rather than assigning to global names jQuery passes a reference to itself into RequireJS and leaves the global object untouched. That way RequireJS contains the reference to jQuery internally, but the global.requirejs variable is modified instead.

Creating your own non-conflicting global name for your jQuery version and your closure to "rendezvous" on is analogous to RequireJS' functionality. The name RequireJS uses is the "global name": requirejs.s.contexts._.jQuery (not exactly, but something like this).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mike Edwards
  • 3,742
  • 17
  • 23
  • Wanted to add to the record that I think I'm going to modify my library and expose a single use function to ingest jQuery. based on what you said, I'm assuming this is what require does? – Shane Oct 08 '13 at 20:09
  • requirejs supports a variety of configurations that makes it quite a bit more complex. Generally when you say `var $ = require('jquery')` it uses the current configuration to map the context to requirejs.s.contexts.activeContextName then uses the 'jquery' string to pluck the 'jquery' property from that context assuming it has been loaded. So yes, in some sense require('jquery') "ingests" the pre-loaded jQuery definition stored within the active require context. – Mike Edwards Oct 08 '13 at 20:17
  • I was thinking something like window.myWindowObj.ingestJQuery(jQuery) from inside my controlled version of the jQuery library / closure to keep it off the window object entirely. I wasn't clear about that, do you see any problems with this approach? – Shane Oct 08 '13 at 20:28
  • 1
    It's not critical that you stay off the window object, just that any fields you create have reliably non-conflicting names. One way to avoid conflicts is to stuff those names inside another object, like requirejs or "myWindowObj". Another way is to use strange randomly generated names. Your solution should work just fine, assuming you ensure proper load/execution order. – Mike Edwards Oct 08 '13 at 20:42
  • Not to digress, but some of our saas customers get a little upset if the use of the window object isn't limited as much as possible, particularly those concerned with XSS, etc. Any exposed methods on an object have to sanitize inputs, etc. very enlightening answer, thanks! – Shane Oct 08 '13 at 20:52
  • The one concern that just occurred to me is that jQuery has no dependencies on your library, while your library has a dependency on jQuery. Adding code to the jQuery file that depends on your library having loaded produces a circular dependency that could complicate loading issues, while using a global name ensures that load jQuery > then load library is a valid order. – Mike Edwards Oct 08 '13 at 20:52
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/38825/discussion-between-shane-and-mike-edwards) – Shane Oct 08 '13 at 20:53
3

Call jQuery.noConflict(true) and it will re-assign $ and jQuery to wherever it was.

Then, you can pass the reference returned by the above expression into your closure.

(function(jQuery) {

})(jQuery.noConflict(true));
alex
  • 479,566
  • 201
  • 878
  • 984
  • So this makes it possible to use multiple versions of jQuery? There are many scripts that use specific versions of jQuery. –  Oct 08 '13 at 19:25
  • If you don't use `true`, `window.jQuery` will not be reverted to what it was previously, which can cause problems when dealing with multiple versions of jQuery. – Kevin B Oct 08 '13 at 19:29
  • @KevinB Good point, I will include it in my answer and upvote yours. – alex Oct 08 '13 at 19:53
2

Yes, calling jQuery.noConflict(true) will completely remove anything that jquery added from the global namespace reverting it back to what it was previously allowing for conflicting libraries and/or multiple versions of jQuery.

<script src="jquery.min.js"></script>
<script>
(function($){
    $("el").doSomething();
})(jQuery.noConflict(true));
</script>
Kevin B
  • 94,570
  • 16
  • 163
  • 180