Using jQuery As A Named Module In RequireJS

Posted November 16, 2011 at 10:53 AM by Ben Nadel

Tags: Javascript / DHTML

With the release of jQuery 1.7, there's been some amorphous buzz about the support between AMD (Asynchronous Module Definition) script loaders and the jQuery library. I say, "amorphous," because I simply don't understand what that support entails. While reading Wilson Page's post on loading AMD modules from a remote CDN (Content Delivery Network), however, I started to see that "named modules" seemed to be important. And, furthermore, I discovered, after some testing, that RequireJS supports using a named "jquery" module even prior the release of jQuery 1.7.

In RequireJS, you can load JavaScript dependencies before a given block of code needs to be executed. These dependencies can be defined in the form of file paths to the dependent modules; or, they can be defined by aliases (ie. named modules) that represent short-cuts to module file paths.

In my first post on RequireJS, I loaded jQuery using a file path; and, I explained that since jQuery was not an AMD module, it was loaded into the global namespace and not passed through to the require() or define() callbacks. As I was experimenting with Wilson Page's code, however, I discovered that RequireJS will pass jQuery as a dependency parameter if the jQuery dependency is loaded using the named module, "jquery".

With jQuery 1.7, this named module works because jQuery actually calls define() internally to define itself as the "jquery" module:

jquery-1.7.js (Lines 969-971)

  • if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
  • define( "jquery", [], function () { return jQuery; } );
  • }

As you can see, jQuery checks for the existence of the define() function; and, if it exists, it defines the "jquery" module as referencing the jQuery object.

Since most jQuery files are not named, "jquery.js", we can't simply load the jQuery file and use the named module. In order to actually invoke the jQuery dependency as the "jquery" module, we have to create a "jquery" alias to the jQuery file. This will allow us to load the appropriate jQuery file using the simplified name:

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Loading jQuery As A Named Module In RequireJS</title>
  •  
  • <!-- Include the require JS library. -->
  • <script type="text/javascript" src="./require.js"></script>
  • <script type="text/javascript">
  •  
  •  
  • // Configure the RequireJS paths to use an alias for the
  • // jQuery library.
  • require.config({
  • paths: {
  • "jquery": "./jquery-1.7"
  • }
  • });
  •  
  •  
  • // Now that we have configured a named alias for the jQuery
  • // library, let's try to load it using the named module.
  • require(
  • [
  • "jquery"
  • ],
  • function( $17 ){
  •  
  • // Log the callback parameter.
  • console.log( "$17.fn.jquery:", $17.fn.jquery );
  •  
  • }
  • );
  •  
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left intentionally blank. -->
  • </body>
  • </html>

As you can see, I am mapping the file, "jquery-1.7.js" to the alias, "jquery." Then, when I execute my require() function, I can use the named module, "jquery," to reference my jQuery 1.7 instance; and, since jQuery 1.7 registered itself as the named module, "jquery", running the above code gives us the following console output:

$17.fn.jquery: 1.7

jQuery 1.7 was successfully loaded using the named module, "jquery," and passed-through to the require() callback as a dependency argument.

I think this is what the whole jQuery 1.7 AMD support is all about - this internal call to define the "jquery" module. But, after poking around in the RequireJS library, I discovered that this approach also works for version of jQuery prior to 1.7 (including the bundled version of RequireJS and jQuery 1.6.4). The reason for this is that the RequireJS library has special internal checks for the jQuery library that also registers the jQuery object as the named module, "jquery."

This means that if we are using a version of jQuery prior to 1.7, we get the same benefit; only, the define() call is executed by the RequireJS library, not the jQuery library.

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Loading jQuery As A Named Module In RequireJS</title>
  •  
  • <!-- Include the require JS library. -->
  • <script type="text/javascript" src="./require.js"></script>
  • <script type="text/javascript">
  •  
  •  
  • // Configure the RequireJS paths to use an alias for the
  • // jQuery library.
  • require.config({
  • paths: {
  • "jquery": "./jquery-1.6.4"
  • }
  • });
  •  
  •  
  • // Now that we have configured a named alias for the jQuery
  • // library, let's try to load it using the named module.
  • require(
  • [
  • "jquery"
  • ],
  • function( $164 ){
  •  
  • // Log the callback parameter.
  • console.log( "$164.fn.jquery:", $164.fn.jquery );
  •  
  • }
  • );
  •  
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left intentionally blank. -->
  • </body>
  • </html>

As you can see, we are using the same exact approach; only, we're loading jQuery 1.6.4 instead of jQuery 1.7. And, when we run the above code, we get the following console output:

$164.fn.jquery: 1.6.4

The jQuery 1.6.4 instance was successfully loaded and passed-through to the require() callback as a dependency argument.

This is really cool and allows us to use dependency arguments instead of globally available variables. Just be careful if you start trying to load different versions of jQuery simultaneously; between the jQuery 1.7 internal define() call and the pre-1.7 define() call made implicitly by RequireJS, you're bound to start overriding the meaning of the "jquery" named module.

I know this seems like an awful lot of explanation for something so tiny; but, every time we can disconnect ourselves from a global variable and attach ourselves to a local variable, we increase the modularity and cohesiveness of our code. This ultimately means less bugs and easier maintenance. And, not to mention, it's probably important to know how some libraries work internally so you can figure out why some unexpected behaviors may occur.




Reader Comments

Nov 16, 2011 at 11:19 AM // reply »
8 Comments

Ben,

As always - great post.
I'm an avid reader, and a huge fan.

I've been meaning to stop being lazy and begin using something like RequireJS before my new project I'm working on becomes unworkable...

Just so intimidating to go down a new path...

Hoping you inspire me to move forward.


Nov 16, 2011 at 11:23 AM // reply »
11,246 Comments

@Joshua,

I've only just started playing with RequireJS; but, I'm already really liking it. Like you, I haven't done anything with it in a production app. But, I definitely want to start integrating it in areas where it doesn't necessarily require a full rewrite of existing code (baby steps).

The modularity of the code is really cool, though, and it loads things quickly. I hope I can keep writing about this stuff, keep the inspiration coming ;)


Sam
Nov 16, 2011 at 12:37 PM // reply »
6 Comments

Honestly - IMHO it's better to wrap jQuery library into define() call and use it as a normal unnamed module. You really only have to do couple changes - like removing a line where jQuery attaches itself to window object.


Nov 16, 2011 at 1:52 PM // reply »
5 Comments

Hi Ben, I am the main developer of requirejs. I have really enjoyed your posts.

You are correct that requirejs will "autodetect" previous jQuery versions before 1.7, but other AMD module loaders do not do that, so it is best that jQuery actually call define.

So, the requirejs behavior is non-standard for AMD loaders. I did it to help bootstrap module loading for jQuery based projects, but I hope to remove that special detection over time so that I conform more with other AMD loaders.

jQuery calls define() as a named module because using an anonymous module could cause an error in an AMD loader if the file was loaded outside of a module loader call (like if the developer just puts in a plain script tag for the file). This could happen if there is a standard analytics package on the page in part of the page template the AMD-based developer does not control.

Also, to get the same jQuery module for multiple files that have jQuery as a dependency, they all need to agree on the same dependency name, which should be 'jquery'.

So for those reasons, and since jQuery by definition sets the implementation expectations for a dependency called 'jquery', it is why jQuery registers as a named module.

However, for other scripts, say Zepto, who aim to provide the same interface as jQuery, they would call define as an anonymous module so that it could be mapped by the loader to satisfy the 'jquery' dependency.


Nov 17, 2011 at 9:26 AM // reply »
11,246 Comments

@Sam,

Are you suggesting that the jQuery team do this? Or that I should go into the source code and do this for my projects?

@James,

First off, awesome library! I only started looking into it a week or two ago and really started liking it right off the bat. I'm always surprised how easy it is to just get things working! Well done, sir!

As far as the Zepto thing, you would have to use the "paths" config, right? To map the Zepto JavaScript file to the "jquery" dependency? Or were you just using "jquery" as an example? I'm not familiar with Zepto, so I am not really sure what it is.

Anyway, awesome stuff! I can't wait to try building more complex learning apps with this!


Nov 17, 2011 at 9:31 AM // reply »
54 Comments

How about showing how you require jquery from a cdn.

@ Mr. Burke,
Does this work work with r.js? I've traditionally had issues when building with code outside define calls.


Nov 17, 2011 at 9:37 AM // reply »
11,246 Comments

@Drew,

Take a look at Wilson Page's post - he is loading jQuery from a CDN:

http://wilsonpage.tumblr.com/post/12842562690/requirejs-getting-amd-modules-from-a-cdn

That post is how I started to make the connection between the named module and the loader.

As far as r.js, I'll defer to James; I've got no experience as of yet with the optimizer.


Nov 17, 2011 at 11:07 AM // reply »
4 Comments

Hey Ben,
there's nothing wrong with explaining something in detail :)

I just starting using AMD modules, incl. jQuery 1.7, on a site. Since I had written the original code using the module pattern, it was actually kinda easy to make the switch.

The thing I'm still not sure about is how to handle the jQuery plugins. One suggestions on the require.js site is to simply wrap them in a define function. But that only worked for a very small one for me. And the pattern described by Addy Osmani in his Smashing Mag article (http://www.smashingmagazine.com/2011/10/11/essential-jquery-plugin-patterns/) is an approach which would require a rewrite of existing plugins.

My workaround for the moment is that I store a reference to the AMD jQuery version (I would normally pass in as a dependency to define) in a variable, and pass this reference to the plugins (see this article by Addy Osmani: http://addyosmani.com/blog/jquery-17-preview/). But I'm gonna keep experimenting...


Nov 17, 2011 at 1:11 PM // reply »
11,246 Comments

@All,

I finally played around with the build tool and got it work!

http://www.bennadel.com/blog/2288-My-First-Look-At-The-RequireJS-Build-Optimizer-For-Node-js.htm

The only thing I had trouble with was "text!" dependencies in which the resource file path started with "./". In such cases, it looks like the resource name normalization prevents the content from being cached. Otherwise, it's pretty badass!!


Nov 17, 2011 at 1:14 PM // reply »
11,246 Comments

@Oliver,

Yeah, the plugins definitely present a problem, especially when we are not the one writing them (and they may be minified and obfuscated and what not). I'll have to check out the links you posted. The easy thing with the "traditional" model is that you just work with the global jQuery variable and the plugins automatically hook in properly. With modular architecture, it becomes a bit more funky. Though, I see, even with the AMD jQuery 1.7 stuff, jQuery is *still* added to the global namespace.

More stuff to explore!


Nov 17, 2011 at 3:29 PM // reply »
5 Comments

@Ben: you are correct, to use Zepto in place of jQuery (with Zepto ideally calling define() as an anonymous module) then you would use the paths config to point the 'jquery' module ID to the 'zepto' path.

@Drew: In your build config, if you set a paths: { 'jquery': 'empty:' then it will not try to inline the dependency from the CDN. This assumes you have 'jquery' paths config set to the CDN location in your web page. If you have problems with that, please let me know via an r.js github issue.

@Oliver: you might want to look at the README in this project for ideas around using the priority config or the order! plugin when using jQuery plugins that do not call define but expect jQuery to be immediately available:

https://github.com/jrburke/require-jquery

If you are writing your own jQuery plugin, this gist might also be a way to optionally call define() if it is available:

https://gist.github.com/1344389


Nov 17, 2011 at 6:04 PM // reply »
4 Comments

@Ben: Yeah, it won't get boring any time soon ;)

@James: Thanks for the link to the boilerplate! Didn't work for the two plugins I'm using right away, but I'll take a closer look later.


Dec 26, 2011 at 6:34 AM // reply »
3 Comments

Hello Team, I am totally new to ColdFusion. We are working on CF9 version.Couple of weeks back in our application cfmail notification is not working. The error message what is logged is:

An exception occurred when setting up mail server parameters. This exception was caused by: coldfusion.mail.MailSpooler$SpoolLockTimeoutException: A timeout occurred while waiting for the lock on the mail spool directory

After lot of google search i find that the common answer provided for this is , by retsarting the Cf instance it gets vanished for sometime only but again it might occur.

Can anyone please tell me what if the proper solution for this? This is a PROD issue we need to fix asap.

Thanks a lot in advance,
Nagamma M


Dec 27, 2011 at 1:06 AM // reply »
3 Comments

Hello Sir,

Thank you for your response.
The application which i am working on is running from past 5 to 6 years and it has become stable almost now. There are no change request expecting further into this application. This is an existing application issue which never occured earlier , so i need the solution on this.

I am wondering is there any changes which can be done on configuration side, bcoz at present i can see the spool interval time defined at admin setting sis 15sec, is there any other solution other then re-starting the CF instance. As i can read from google that it might agian occur after even re-starting also !.

Your inputs would be appreciated.

Thanks
Nagamma


Jan 5, 2012 at 12:40 PM // reply »
1 Comments

Name modules has been causing me mass confusion for the last 3 day. The two following links have cleared everything up.

AMD modules with named defines. So much pain for what gain?
http://dvdotsenko.blogspot.com/2011/12/amd-modules-with-named-defines-so-much.html

Register as an anonymous module (James Burk)
https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon


Jan 10, 2012 at 1:05 AM // reply »
3 Comments

@Joe shmoe

Thank you for your response.
The application which i am working on is running from past 5 to 6 years and it has become stable almost now. There are no change request expecting further into this application. This is an existing application issue which never occured earlier , so i need the solution on this.

I am wondering is there any changes which can be done on configuration side, bcoz at present i can see the spool interval time defined at admin setting sis 15sec, is there any other solution other then re-starting the CF instance. As i can read from google that it might agian occur after even re-starting also !.

Your inputs would be appreciated.

Thanks
Nagamma


Jan 24, 2013 at 2:07 PM // reply »
1 Comments

Let me prologue this by saying (requireJs is awesome) I have a config.js that works great, I'm extremely happy with it but I want to optimize it and reduce the requests that my page is making.

From terminal Im calling `r.js config.js` and its throwing `Error: Calling node's require("jquery") failed with error: Error: Cannot find module 'jquery'`

I love these build tools, optimizers, etc... but it seems I need a little help here. I'm confused by this statement:

"@Drew: In your build config, if you set a paths: { 'jquery': 'empty:' then it will not try to inline the dependency from the CDN. This assumes you have 'jquery' paths config set to the CDN location in your web page"

What is the build config? Do I set "paths config" in my webpage? What if I want to upgrade jquery to v22.rc5? Do I have to edit each of these html files?

And no offense intended here, I know I am 'doing it wrong', because the entire point of dependency mgmt is rolling the dependencies into the file that needs them and configuring your libraries in one place. That way if you refactor your base class you don't have to run through 50+ html files to change, add or remove the links. (If you manage that with CGI, please gracefully ignore that comment.) Also you don't have to have all those non-optimized resources sitting on top of your app in places where they aren't required. right?

Here's a sample of my files:
** Fiddle: http://jsfiddle.net/SDbUg/
** Gist: https://gist.github.com/4625559

I've also posted the question to SO
http://stackoverflow.com/questions/14507995/requirejs-with-cdn-dependencies

shorry but mah headsh shpinning off :)



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools