Designing jQuery Plugins: An Introduction to The Module Design Pattern

Designing jQuery Plugins: An Introduction to The Module Design Pattern

Great design is a product of care and attention applied to areas that matter, resulting in a useful, understandable, and hopefully beautiful user interface. But don’t be fooled into thinking that design is left only for designers.

Anthony Colangelo

I’ve recently been working on a lot of JS projects from application development to optimization. When starting to code JS, there’s as many resources and opinions as there are grains on sand when it comes to how to format, or design your code. From starter templates to design patterns, it seems everyone has a solution for the proper way to code JS. In reality, like with all programming, there’s no one right answer. Instead, there’s basically three aspects to keep in mind and if implemented properly, your code will come out clean, well-optimized and beautiful.

  1. Architecture—The layout of your code. It’s like the foundation and skeleton of a house. It’s one of the most important aspects when building scripts. It should define rules that govern how components like models, views, and controllers interact with each other.
  2. Maintainability—Ensure your code can be easily improved and extended. In my opinion this is the second most important aspect when building JS applications. Don’t build your application so it’s locked down from improvement. Make sure you can teach your old dog new tricks.
  3. Reusability—Like with most programming, reusability is another important aspect when building your application. It’ll save you time and make maintaining it make easier.

When coding JavaScript, it’s pretty easy to get messy quick. Since it’s a loose language, it’s easy to get carried away adding bits and pieces here and there without it killing your script. That’s why architecture is one of the most important aspects when coding. Establish a framework and stick to it. It’ll provide constraints to your code to help ensure consistency throughout.

So what’s the best approach? Well, like I said, there’s no one right answer. The approach I’m most fond of is the tried-and-true software module design pattern. It’s structure is a solid system architecture and allows for a maintainable codebase. This pattern is most often used in jQuery plugins, which provides developers robust options, ease of maintainability, reusability, and exposes a well-crafted API.

Let’s take a look at this approach below while I walk you through how to design your code into a well-organized application that can be easily reused in projects to come.

An Introduction to The Module Design Pattern

To help walk you through the module design pattern, I’ll take an example from a recent JS application I built that uses my PHP Stock Market API to grab real-time market data and then display a nice little interface with that data.

var rts = {
  getData: function() {
    ...
  },
  updateSymbol: function() {
    ...
    this.getData();
  }
}

Looking at the code above, you can see that everything is nicely separated and broken down to it’s smallest parts allowing for the most reusability. I could have just created a single function that gets the data and then updates the symbol, but creating distinct methods provides more control and reusability within the module.

Something to take note of is how the methods and properties are called within the module itself. Their prefixed with the this keyword—that’s how modules access their own members. Same as building PHP classes.

That’s the basic structure of a module that you can continue to add methods and properties to as needed. After the architecture is built, the reusability layer (options and an exposed API) can be built on top.

jQuery Plugin Module Design Pattern

I’m a big fan of jQuery and one of the main reasons why, are the plugins. jQuery plugins make it easy to build reusable, complex applications, and allows developers to easily access those applications without a lot of research on how to set it up. The implementation of the code will be consistent with the rest of jQuery-based projects which is good for aesthetic reasons, but more importantly, developers can predict how to interact with it. It’s just another way to build a better developer interface.

Before building any jQuery plugin, it’s important to ensure the plugin doesn’t conflict with other JavaScript libraries using the $ notation. An easy thing to do with just a few lines of code:

;(function($, window, document) {
  // jQuery plugin code here
})(jQuery, window, document);

The semi-colon before the function isn’t a typo. It’s a safety net against concatenated scripts and/or other plugins that haven’t been closed properly. window and document are passed as local variables rather than globals, because it (slighty) quickens the resolution process and can be more efficiently minified (especially when both are regularly referenced in your plugin).

Next, we’ll set up our plugin by simply dropping our previously built module code inside. A plugin is just a method defined on the jQuery ($) object.

;(function($, window, document) {
  $.realTimeStocks = function() {
    var rts = {
      getData: function() {
        ...
      },
      updateSymbol: function() {
        ...
        this.getData();
      }
    };
  };
})(jQuery, window, document);

With the code above, it’s as simple to call the plugin:

var rts = $.realTimeStocks();

A (jQuery) plugin is just a method defined on the jQuery ($) object.

jQuery Plugin Options

Options are what make jQuery plugins truly reusable, because they allow for customizations for each implementation. No one project is built the same and will require customizations to suit the developer’s needs. Customizable options help ensure that developers can do that.

Also, be sure to provide default values for your options so your code is clean and won’t break should an option be required, but not set. With jQuery, it’s easy to do this with the $.extend() method.

;(function($, window, document) {
  $.realTimeStocks = function(options) {
    var rts = {
      options: $.extend({
        'updateDuration': 3000,
        'postURL': 'stockMarketAPI.php'
      }, options),
      getData: function() {
        ...
      },
      updateSymbol: function() {
        ...
        this.getData();
      }
    };
  };
})(jQuery, window, document);

The first argument of $.extend(), defines an object with all the available options and their default values. The second argument, passes through the passed-in options. This will effectively merge the two objects, overriding the defaults with any passed-in options.

An added benefit to providing default options is they almost become self-documenting. Developers can easily look at the code and see what options are available. Exposing as many options as possible will allow for more customization and help the flexibility of the plugin and future implementations.

jQuery Plugin API

A key component to any jQuery plugin is the ability to extend what it can do. Exposing the plugins methods and properties allows developers to build on and customize the plugin to suit their needs. Though, be sure to only expose the elements that will be used. The outside world shouldn’t have access to all internal methods and properties.

In the example above, the exposed API should only include a call to the updateSymbol() method. The internal getData() method runs automatically when the symbol get’s updated so the public doesn’t need access to it.

Exposing an API requires you return an object with any of the desired methods and properties at the end of the plugin code. The best part of this is you can return methods and properties within the module code—this is the beauty of a well-organized module pattern.

;(function($, window, document) {
  $.realTimeStocks = function(options) {
    var rts = {
      options: $.extend({
        'updateDuration': 3000,
        'postURL': 'stockMarketAPI.php'
      }, options),
      getData: function(sym) {
        ...
      },
      updateSymbol: function(sym) {
        ...
        this.getData(sym);
      }
    };
  };
  
  return {
      update: rts.updateSymbol(sym),
      someMethod: function( ) { … }
  }
})(jQuery, window, document);

API method and properties are now available through the object returned from the plugin initialization.

var rts = $.realTimeStocks({
  updateDuration: 5000,
  ...
});

rts.update('AAPL');

A Beautiful jQuery Plugin

With the module pattern described above, we’ve got ourselves a beautiful, reusable, extensible plugin that will make developers lives easier. Like with any project, experiment with this structure so it works for you, your team, and your workflow. Also, check out this great jQuery lightweight plugin boilerplate I came across by Addy Osmani that implements the module design pattern.

/*!
 * jQuery lightweight plugin boilerplate
 * Original author: @ajpiano
 * Further changes, comments: @addyosmani
 * Licensed under the MIT license
 */


// the semi-colon before the function invocation is a safety 
// net against concatenated scripts and/or other plugins 
// that are not closed properly.
;(function ( $, window, document, undefined ) {
    
    // undefined is used here as the undefined global 
    // variable in ECMAScript 3 and is mutable (i.e. it <span class="hiddenGrammarError" pre="and "><span class="hiddenGrammarError" pre="and ">can 
    // be</span></span> changed by someone else). undefined isn't really 
    // being passed in so we can ensure that its value is 
    // truly undefined. In ES5, undefined can no longer be 
    // modified.
    
    // window and document are passed through as local 
    // variables rather than as globals, because this (slightly) 
    // quickens the resolution process and can be more 
    // efficiently minified (especially when both are 
    // regularly referenced in your plugin).

    // Create the defaults once
    var pluginName = 'defaultPluginName',
        defaults = {
            propertyName: "value"
        };

    // The actual plugin constructor
    function Plugin( element, options ) {
        this.element = element;

        // jQuery has an extend method that merges the 
        // contents of two or more objects, storing the 
        // result in the first object. The first object 
        // is generally empty because we don't want to alter 
        // the default options for future instances of the plugin
        this.options = $.extend( {}, defaults, options) ;
        
        this._defaults = defaults;
        this._name = pluginName;
        
        this.init();
    }

    Plugin.prototype.init = function () {
        // Place initialization logic here
        // You already have access to the DOM element and
        // the options via the instance, e.g. this.element 
        // and this.options
    };

    // A really lightweight plugin wrapper around the constructor, 
    // preventing against multiple instantiations
    $.fn[pluginName] = function ( options ) {
        return this.each(function () {
            if (!$.data(this, 'plugin_' + pluginName)) {
                $.data(this, 'plugin_' + pluginName, 
                new Plugin( this, options ));
            }
        });
    }

})( jQuery, window, document );

Author: Ben Marshall

Red Bull Addict, Self-Proclaimed Grill Master, Entrepreneur, Workaholic, Front End Engineer, SEO/SM Strategist, Web Developer, Blogger

Leave a Reply

Your email address will not be published. Required fields are marked *