88

I'm working with requirejs+jquery and i was wondering if there was a smart way to make a jQuery plugin work well with require.

For example i'm using jQuery-cookie. If i understood correctly i can create a file called jquery-cookie.js and inside do

define(["jquery"], // Require jquery
       function($){
// Put here the plugin code. 
// No need to return anything as we are augmenting the jQuery object
});
requirejs.config( {
    "shim": {
        "jquery-cookie"  : ["jquery"]
    }
} );

i wondered if i could do things like jQuery does, which is like this:

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

or if this is the only way to make jQuery plugins compatible with requirejs or any amd

3 Answers 3

104

There are some caveats with using shim configuration in RequireJS, pointed out on http://requirejs.org/docs/api.html#config-shim. Namely, "Do not mix CDN loading with shim config in a build" when you're using the optimizer.

I was looking for a way to use the same jQuery plugin code on sites both with and without RequireJS. I found this snippet for jQuery plugins at https://github.com/umdjs/umd/blob/master/jqueryPlugin.js. You wrap your plugin in this code, and it will work properly either way.

(function (factory) {
if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module depending on jQuery.
    define(['jquery'], factory);
} else {
    // No AMD. Register plugin with global jQuery object.
    factory(jQuery);
}
}(function ($) {

    $.fn.yourjQueryPlugin = function () {
        // Put your plugin code here
    };  

}));

Credit goes to jrburke; like so much javascript, it's functions inside functions acting on other functions. But I think I have unpacked what it's doing.

The function argument factory in the first line is itself a function which is invoked to define the plugin on the $ argument. When no AMD-compatible loader is present, it's invoked directly to define the plugin on the global jQuery object. That's just like the common plugin definition idiom:

function($)
{
  $.fn.yourjQueryPlugin = function() {
    // Plugin code here
  };
}(jQuery);

If there is a module loader, then factory is registered as the callback for the loader to invoke after loading jQuery. The loaded copy of jQuery is the argument. It's equivalent to

define(['jquery'], function($) {
  $.fn.yourjQueryPlugin = function() {
     // Plugin code here
  };
})
Sign up to request clarification or add additional context in comments.

7 Comments

I like this answer because even though the question was about a jQuery plugin, this works for any sort of module or library, not just jQuery. It took me a minute to wrap my head around your code, but once I did, it made perfect sense.
It's definitely impenetrable javascript. I added some explanation about how it works.
Any tips on how to do this and make a plugin that works with jQuery or Zepto? You could easily replace the non-AMD code with factory(jQuery || Zepto);, but I don't know how you would do the AMD part. I think you would have to set the jQuery "path" to zepto?? paths: { jquery: 'vendor/zepto/zepto.min' }
wow I had long been wanting understand this snippet of code. Thanks very much, Isn't the shim essentially doing just that? I guess not.. because you said you are manually coding it like the above.
Protip for idiots like me: where it says $.fn.jqueryPlugin in the snippet, change jqueryPlugin to the plugin name!!
|
67

You only need to do EITHER

define(["jquery"], // Require jquery
       function($){
// Put here the plugin code. 
// No need to return anything as we are augmenting the jQuery object
});

at the end of jquery-cookie.js, OR

requirejs.config( {
    "shim": {
        "jquery-cookie"  : ["jquery"]
    }
} );

anywhere before you include jquery-cookie (like wherever data-main points to, for instance).

The last code block you've posted is good for things like jQuery which get redistributed and may or may not be in an AMD environment. Ideally every jQuery plugin would have that set up already.

I prefer to keep included libraries as unadulterated as possible, so the global shim config once per page seems like the cleanest solution to me. That way upgrades are safer and CDNs become a possibility.

4 Comments

If i don't do both the define and shim things don't work, i get a $.cookie is not a function error message
To my experience, not returning anything from the defining function is not sufficient, requirejs will not recognize that the module loaded. This can be woked around by shim: { plugins : { depends: ['jquery'], exports: 'plugins' }
You should note that the jQuery plugin script needs to actually run. I am no expert but I found a straightforward solution whereby you have a single require(['plugin1', 'plugin2']); etc. No callback is passed in so nothing will run except the plugin scripts themselves. This means that they will then add themselves to jQuery.fn so everywhere else you can just list 'jquery' as a dependency and they will be included. Like I said, I am pretty new to this and there may be a better approach (such as just using a script tag) but this does work.
Josh: You're gambling that everything is going to load in the right order. Some users on your site are likely seeing broken stuff. The problem isn't as much that the plugin loads, it's when the plugin loads and what else is executing at the time. The main reason to use require is to ensure a sensible order of operations based on dependencies rather than guesswork.
2

Just as an FYI to all some of the jquery plugins already use amd/require so you dont need to declare anything in the shim. In fact doing so might cause problems for you in some cases.

If you look in the jquery cookie JS you will see define calls and require(["jquery"]).

and in jquery.js you will see a call to define("jquery",[],function()...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.