jQuery Plugins

An effective way to extend or enhance jQuery.

Here we examine different jQuery Plugin Patterns and Best Practices, including the 'Mother of all Plugins'.



Overview


The jQuery library is an open system that from the beginning has promoted extensibility and reuse.

A common misconception is that jQuery plugins are only for open source projects to be shared with the community at large. If your motivation is to contribute your plugins or widgets to the open-source community then that is commendable, but don't forget that plugins can be very beneficial to your own projects as well.

Once you get the hang of constructing plugins you will realize that writing these is not much more difficult than writing regular JavaScript or jQuery code. The advantage you're getting is that they seamlessly integrate with jQuery: it is as if your enhancements shipped with the core library itself. A good example of this integration is chaining. Your custom methods can be chained together with any existing jQuery commands.

The flexibility of JavaScript allows you to overwrite, inherit, modify, and extend the jQuery library. However, jQuery provides an extension point, which is the jQuery.fn object where you are expected to add your plugin. By the way: jQuery.fn (or $.fn) is just an alias to jQuery.prototype, so at the end your plugin gets added as a method to jQuery's prototype object.

Common Plugin patterns
There are three commonly used patterns to structure a plugin, they are:

  1. Function Plugin
  2. Extend Plugin
  3. Constructor Plugin

Function Plugin: The simplest (and most popular) way is to directly add a function to $.fn. Here is the code:

$.fn.myPlugin = function() {                   
    alert("My first plugin!");   
};

$.myPlugin();          // => My first plugin!

Here is an example plugin you can use in the real-world:

$.fn.signal = function() {
    this.slideDown(300).delay(1000).slideUp(300);   
};

And this is how you use it:

<div id="flash"></div>    <!-- hidden red div -->
$("#flash").signal();

Click on the button to get the signal() started.




This works great. It demonstrates how a simple plugin can make your jQuery life a whole lot easier. There is no point in sharing the above plugin as an open source project, but for your own purposes it is very useful.

A weakness of the above example is that it relies on $ being an alias for jQuery which may not always be the case. To protect against this you can wrap the plugin in an anonymous immediate function.

(function ($) {
    $.fn.myPlugin = function () {             
        alert("My first plugin!");   
    };
})(jQuery);

$.myPlugin();          // => My first plugin!

Much better: we can now be sure that within the closure the $ always refers to jQuery.


Extend Plugin: As an alternative you can use the jQuery's extend method:

(function ($) {
    $.extend($.fn, {                         // Extend
        myPlugin: function () {
            alert("My first plugin!");   
        }
    });
})(jQuery);

$.myPlugin();          // => My first plugin!

The extend method extends $.fn with the properties in the object literal that is provided. Using extend allows you to assign multiple properties and methods in a single statement.

We should caution against adding multiple names to the $.fn namespace as this can lead to name collisions with jQuery itself or other plugins. You should consider wrapping your code base in its own namespace with a name that is unlikely to conflict with other names that may exist. You could use your project's name, company name, or domain name.

(function ($) {
    $.fn.MyCompany = {};
    $.extend($.fn MyCompany, {
        start: function () { alert("start "); },   
        stop: function () { alert("stop "); },   
        enable: function () { alert("enable "); },   
        disable: function () { alert("disable "); },   
    });
})(jQuery);

$.MyCompany.start();        // => start
$.MyCompany.enable();       // => enable

Here we created a namespace called MyCompany. It is the only name we're adding to $.fn. All our methods and properties are inside the MyCompany namespace and our impact on jQuery's fn namespace is now minimal.


Constructor Plugin: A third alternative is to use a private constructor function. Here is an example:

(function ($) {
    var Plugin = function (element) {        // Constructor
        this.element = element;
        this.$element = $(element);

        this.doSomething = function() {
            // ...
        }
    }

    $.fn.myPlugin = function () {
        return this.each(function() {
            if (!$.data(this, "myPlugin")) {
                $.data(this, "myPlugin", new Plugin(this)); 
            }
        };
    }
})(jQuery);

This pattern does more than just assigning a plugin object to $.fn. It also associates an object (a Plugin instance) with each element in jQuery's result set.

Plugin is a constructor function that accepts a DOM element as its argument. The function's name is Plugin but could be anything because it is never exposed to the outside world. The real plugin name is the one you assign to $.fn (myPlugin in our example). In Plugin we declare and initialize all necessary properties and methods.

The actual plugin function is added to $.fn. The context of the function is jQuery's result set which is returned to allow chaining with other jQuery methods. Just before returning this it iterates over each element in the result set using jQuery's built-in each method. The callback function that is passed into each checks if the element already has an attached Plugin instance; if not it creates one and stores it with the element. The data method in jQuery is a utility feature that stores arbitrary data with a specified element. Please note that within the callback function the value of this is bound to an element and not the result set.

By associating a Plugin instance to each element, we have the ability to maintain plugin state for each element. The state is nicely packaged in a single object, rather than a set of name/value pairs. It also makes the plugin easily reachable for each element, like so:

$("selector").data("myPlugin").doSomething();


Constructor Plugin Template
Following the three jQuery plugin patterns we'll now review a more comprehensive plugin that is based on the Constructor Plugin and that you can use as a template for your more advanced plugin projects. This template has evolved as a best-practice model and it is based on experiences from numerous members in the jQuery community. You'll recognize several other patterns and practices in this code.

Here's what the Plugin Template looks like:

;(function ($, win, undefined) {
    var name = "myPlugin",
        defaults = {
            option1: "value1", 
            option2: "value2" 
        };
        
    var Plugin = function (element, options) {
        this.element = element;
        this.$element = $(element);

        // handle different argument options
        if (typeof options === "object")
            this.settings = $.extend({}, defaults, options || {});
        } else if (typeof options === "string") {
            // any special string value processing ...
        }
        this._name = name;
        this._defaults = defaults;

        this.init();
    }

    Plugin.prototype.init = function () {
        // initialization code
    }
    
    $.fn[name] = function (options) {
        return this.each(function() {
            if (!$.data(this, name)) {
                $.data(this, name, new Plugin(this, options)); 
            }
        };
    }

})(jQuery, window);

The first thing you notice is the leading semicolon. The Leading Semicolon idiom prevents improperly closed code from interfering with our module.

The list of parameters in the immediate function (the Module Pattern) has increased to three. The $ parameter ensures that $ is an alias for jQuery within the module. The window parameter is the global object and is available as a local argument which reduces the scope traversing of the JavaScript runtime. Finally, the undefined parameter ensures that undefined really means undefined and not something else (in case some hacker decided to reassign its value).

Next, we have the Single var pattern in which two variables are declared and initialized: name, which is the name of the plugin and defaults which holds name/value pairs with default values for all options. The defaults variable is part of the Options Hash idiom we discussed in the Essentials section. Note that the Single var is not 'pure' because there is another var for the Plugin function.

The Plugin function is a constructor function. It accepts a DOM element and a set of options. The function's name is Plugin but it can be anything else because its name is not exposed to the outside world. Remember, the real plugin name is stored in the name variable.

In Plugin we declare and initialize all necessary properties: element and $element (we save element as both a normal reference and a jQuery reference to speed up processing); the settings (built using the Options Hash idiom), and two private properties _name and _defaults which store the plugin name and default values locally. Finally, the init method is invoked which will initialize the plugin further now that all properties are properly set.

To conserve memory all Plugin methods are associated with its prototype rather than with the instance itself. This ensures that all methods are shared. Here we only have an init method but you can add additional methods if necessary.

At the end of the template we are adding the plugin function to jQuery's $.fn. This function has a single parameter named options. There is no need for an element parameter because the context (this in the function) refers to the result set which has all relevant elements.

The remainder of the plugin function is similar to the earlier Constructor Plugin Pattern. The only difference is that here we are passing an extra options argument into the new Plugin constructor call.


Mother of all Plugins
After you have written several plugins using the above boilerplate templates you may find that the process becomes rather repetitive. You are wondering if there is not a more generic solution to writing plugins: something along the lines of a base class (or prototype in JavaScript) which has all the boilerplate code and you just fill in the details. It turns out there is indeed a more generic solution which has been referred to as the DOM-to-Object Bridge Pattern. This sounds awkward so we will refer to it as the Mother of all Plugins pattern.

Actually, if you understand the prior template we discussed, then this pattern is fairly straightforward. You wrap the actual registration with $.fn in a new function called plugin. It accepts the object name which contains the plugin functionality and the rest is quite similar to the template discussed before.

Here is the skeleton code:

;(function ($, win, undefined) {

    $.fn.plugin = function (name, object) {
        $.fn[name] = function (options) {
            return this.each(function() {
                if (!$.data(this, name)) {
                    $.data(this, name, create(object).init(this, options)); 
                }
            };
        },
    }
    function create (obj) { // in newer browsers replace with Object.create
        function F() {};
        F.prototype = obj;
        return new F();
    }

})(jQuery, window);


Your plugin object (which does not have to deal with plugin plumbing) is an object literal that has the following structure (again similar to the Constructor Function plugin patterns):

var myPlugin = {
    init: function (element, options) {
        this.element = element;
        this.$element = $(element);
        this.settings = $.extend({}, this.defaults, options || {} );
        
        _init();
    },
    defaults: {
       color: "Yellow",
       speed: 100;
    },
    _init() {
        // internal initialization
    },
    sayColor() {               // public method
        alert(settings.color);
    }
};

Finally, here is an example of how this is used:

$('selection').plugin("myPlug", myPlugin); // register with plugin plumbing

$('selection').myPlug( {color: "Green" });
$('selection').myPlug.sayColor();          // => Green


Plugin File names Plugins reside in their own JavaScript files and it is important that you follow generally accepted naming conventions. Here are examples of the source and the minified versions:

jquery.pluginname.js
jquery.pluginname.min.js

The names are always in lowercase.

If shared with the open source community, include a version number:

jquery.pluginname-2.1.js
jquery.pluginname-2.1.min.js

If your project team builds multiple plugins, include another namespace which will keep them together:

jquery.projectname.pluginname1.js
jquery.projectname.pluginname2.js



  Adapter