Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Jul. 2009) with: Sean Schroeder

Using jQuery As A Named Module In RequireJS

By Ben Nadel on

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

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.

Reply to this Comment

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

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

@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!

Reply to this Comment

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.

Reply to this Comment

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

Reply to this Comment

@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!

Reply to this Comment

@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

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

@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

Reply to this Comment

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 :)

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.