Skip to content

Instantly share code, notes, and snippets.

@peol
Forked from addyosmani/scraps.md
Created September 14, 2011 17:28
Show Gist options
  • Save peol/1217172 to your computer and use it in GitHub Desktop.
Save peol/1217172 to your computer and use it in GitHub Desktop.
Scraps

/*Copyright Addy Osmani, 2011. Permission is not granted to re-publish this content unless otherwise stated. Its a work in progress :) */

#Essential jQuery Plugin Patterns (WIP)

##Introduction

I occasionally write about implementing design patterns in JavaScript. They're an excellent way of building upon proven approaches to solving common development problems and I think there's a lot of benefit to using them. That said, whilst well-known JavaScript patterns are useful, there's another side of development that can benefit from it's very own set of design patterns - jQuery plugins. The official jQuery plugin authoring guide offers a great starting point for getting into writing plugins and widgets, but let's attempt to take this further.

Plugin development has evolved over the past few years. We no longer have just one way of writing plugins, but many. They can be complex, they can be challenging but at the end of the day they can still be beautiful. I began to think about plugin patterns after seeing a number of efforts made in the past to create a one-size-fits-all jQuery plugin boilerplate. Whilst the idea of such a boilerplate is a great idea in theory, the reality is that in plugin development we rarely approach writing plugins in one very-fixed way using a single pattern all the time.

Some patterns may work better for a particular problem or component moreso than others. Some developers may wish to use the widget factory - it's great for complex, flexible UI components. Some may not. Some might like to structure their plugins more like modules (similar to the module pattern). Some might like to take advantage of nested namespacing patterns to keep things organized. Some might want to use custom events or pub/sub to communicate from plugins to the rest of their app and so on.

Let's assume that you've tried your hand at writing your own jQuery plugins at some point and you're comfortable with putting together something that works. It's functional, it does what it needs to, but perhaps you feel it could be structured better. Maybe it could be more flexible or cater for more concerns. If this sounds familiar and you aren't sure what the differences between many of the different jQuery plugin patterns are, you may find these examples and what I have to say of assistance.

My advice won't provide solutions to every possible pattern, but will attempt to cover popular patterns developers often use in the wild.

#Patterns

jQuery plugins have very few defined rules which is one of the reasons there's an incredible diversity in how we see them implemented. At their most basic, you can write a plugin by simply adding a new function property to jQuery's $.fn object as follows:

$.fn.myPluginName = function() {
    // some plugin logic
};

This is great for compactness, but a better base to build from would be:

(function( $ ){
  $.fn.myPluginName = function() {
    // some plugin logic
  };
})( jQuery );

In the above, to ensure there are no collisions between our plugin and other JavaScript libraries using the dollar sign, we simply pass jQuery to a closure which maps it to the dollar sign to ensure it can't be overridden by anything within its scope of execution.

There's however a lot more that could be done to improve on this and the first pattern we'll be looking at today, the lightweight pattern, covers some best-practices you can use for basic everyday plugin development that takes into account the common gotchas we need to look out for.

####Some quick notes:

Whilst there will be some level of explanation about the pattern boilerplates below, I would recommend reading through the actual code-comments as these offer more insight into why particular best-practices are being employed. Let's go through them now.

I should also mention that none of this would be possible without the previous work, input and advice provided by other members of the jQuery and JavaScript communities. I've listed them inline to each pattern so you can read up on more of their individual work if interested.

###A lightweight start

Let's first begin our look through patterns with something basic that follows best-practices (including those in the jQuery plugin authoring guide). This is an ideal plugin pattern for developers who are either new to plugin development or are just trying to achieve something simple (like a utility plugin). The lightweight start uses:

  • Common best practices such as: a semi-colon before function invocation, [window, document and undefined] passed in as arguments and it also follows the jQuery core style guidelines.
  • A basic defaults object
  • A simple plugin constructor for logic related to initial creation and assigning the element to work with
  • Extension of options with defaults
  • A lightweight wrapper around the constructor which helps avoid issues like multiple instantiations
/*!
 * jQuery lightweight plugin boilerplate
 * Original author: @ajpiano
 * Further changes, comments: @addyosmani
 * Licensed under the MIT license
 */


// the semi-colon before function invocation is a safety net against concatenated 
// scripts and/or other plugins which may not be closed properly.
;(function ( $, window, document, undefined ) {
    
    // undefined is used here as the undefined global variable in ECMAScript 3 is
    // mutable (ie. it can be changed by someone else). undefined isn't really being
    // passed in so we can ensure the value of it is truly undefined. In ES5, undefined
    // can no longer be modified.
    
    // window and document are passed through as local variables rather than globals
    // as 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 which merges the contents of two or 
        // more objects, storing the result in the first object. The first object
        // is generally empty as 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 );

####Further reading: The jQuery Plugin Authoring guide: http://docs.jquery.com/Plugins/Authoring Signs of a poorly written jQuery plugin: http://remysharp.com/2010/06/03/signs-of-a-poorly-written-jquery-plugin/ How to write your own jQuery plugin: http://msdn.microsoft.com/en-us/scriptjunkie/ff608209 Style in jQuery Plugins and Why it Matters: http://msdn.microsoft.com/en-us/scriptjunkie/ff696759 Create your first jQuery plugin - part 2: http://enterprisejquery.com/2010/07/create-your-first-jquery-plugin-part-2-revising-your-plugin/

###Ideal Widget Factory

Whilst the plugin authoring guide is a great introduction to plugin development, it doesn't offer a great number of conveniences for obscuring away from common plumbing tasks we have to deal with on a regular basis.

The jQuery UI Widget factory solves this problem by providing a solution that helps build complex, stateful plugins based on object-oriented principles. It also eases communication with your plugin instance, obfuscating a number of the repetitive tasks you might end up coding when working with basic plugins.

In case you haven't stumbled upon the idea of state before - stateful plugins are plugins which are able to keep track of their current state, also allowing you to change properties of the plugin after the plugin has been initialized.

One of the great things about the Widget factory is that the majority of the jQuery UI library actually uses the factory as a base for its components. That means that beyond this template if you're looking for some further guidance on structure, you don't have to look further than the jQuery UI repo.

Back to patterns. This jQuery UI boilerplate:

  • Covers almost all supported default methods including triggering events.
  • Includes comments for all of the methods used, so you're never left unsure of where logic should fit into your plugin
/*!
 * jQuery UI Widget-factory plugin boilerplate (for 1.8/9+)
 * Author: @addyosmani
 * Further changes: @peolanha
 * Licensed under the MIT license
 */


;(function ( $, window, document, undefined ) {

    // define your widget under a namespace of your choice with additional parameters
    // eg. $.widget( "namespace.widgetname", 
    //     optional - an existing widget prototype to inherit from, 
    //     An object literal to become the widget's prototype
    // ); 

    $.widget( "namespace.widgetname", {

        //Options to be used as defaults
        options: {
            someValue: null
        },

        //Setup widget (eg. element creation, apply theming, bind events etc.)
        _create: function () {

            // _create will automatically run the first time this widget is called
            // Put the initial widget setup code here, then you can access the element 
            // on which the widget was called via this.element
            // The options defined above can be accessed via this.options

            //this.element.addStuff();
        },

        //Destroy an instantiated plugin and clean-up modifications the widget has made to the DOM
        destroy: function () {

            //this.element.removeStuff();
            // For UI 1.8, destroy must be invoked from the base widget
            $.Widget.prototype.destroy.call(this);
            // For UI 1.9, define _destroy instead and don't worry about calling the base widget
        },

        methodB: function ( event ) {

            //_trigger dispatches callbacks the plugin user can subscribe to
            //signature: _trigger(type, event, objectOfKeyValuePairsToPassToCallback)
            this._trigger('methodA', event, {
                key: value
            });
        },

        methodA: function ( event ) {
            this._trigger('dataChanged', event, {
                key: value
            });
        },

        //Respond to any changes the user makes to the option method
        _setOption: function ( key, value ) {
            switch (key) {
            case "someValue":
                //this.options.someValue = doSomethingWith( value );
                break;
            default:
                //this.options[ key ] = value;
                break;
            }

            // For UI 1.8, _setOption must be manually invoked from the base widget
            $.Widget.prototype._setOption.apply( this, arguments );
            // For UI 1.9 the _super method can be used instead
            //this._super( "_setOption", key, value );
        }
    });

})( jQuery, window, document );

####Further reading: http://ajpiano.com/widgetfactory/#slide1 http://msdn.microsoft.com/en-us/scriptjunkie/ff706600 http://wiki.jqueryui.com/w/page/12138135/Widget%20factory http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/

###Namespacing / Nested namespacing

Namespacing your code is an approach for avoiding collisions with other objects or variables in the global namespace. They're important as it's best to safeguard your plugin from breaking in the event of another script on the page using the same variable or plugin names as you are. As a good 'citizen' of the global namespace, it's also imperative that you do your best to similarly not prevent other developer's scripts executing due to the same issues.

JavaScript doesn't really have built-in support for namespaces like other languages, however it does have objects which can be used to achieve a similar effect. Employing a top-level object as the name of your namespace, it's trivial to check for the existence of another object on the page with the same name. If the object doesn't exist, we define it and if it does, we simply extend it with our plugin.

Objects (or rather, object literals) can be used to create nested namespaces such as namespace.subnamespace.pluginName and so on, but to keep things simple, the namespacing boilerplate below should give you everything you need to get started with these concepts.

/*!
 * jQuery namespaced 'Starter' plugin boilerplate
 * Author: @dougneiner
 * Further changes: @addyosmani
 * Licensed under the MIT license
 */

;(function ( $ ) {
    if (!$.myNamespace) {
        $.myNamespace = {};
    };

    $.myNamespace.myPluginName = function ( el, myFunctionParam, options ) {
        // To avoid scope issues, use 'base' instead of 'this'
        // to reference this class from internal events and functions.
        var base = this;

        // Access to jQuery and DOM versions of element
        base.$el = $(el);
        base.el = el;

        // Add a reverse reference to the DOM object
        base.$el.data("myNamespace.myPluginName", base);

        base.init = function () {
            base.myFunctionParam = myFunctionParam;

            base.options = $.extend({}, $.myNamespace.myPluginName.defaultOptions, options);

            // Put your initialization code here
        };

        // Sample Function, Uncomment to use
        // base.functionName = function(paramaters){
        // 
        // };
        // Run initializer
        base.init();
    };

    $.myNamespace.myPluginName.defaultOptions = {
        myDefaultValue: ""
    };

    $.fn.mynamespace_myPluginName = function ( myFunctionParam, options ) {
        return this.each(function () {
            new $.myNamespace.myPluginName(this, myFunctionParam, options);
        });
    };

})( jQuery );

####Further reading: Namespacing in JavaScript: http://javascriptweblog.wordpress.com/2010/12/07/namespacing-in-javascript/ Use your Fn jQuery namespace http://ryanflorence.com/use-your-fn-jquery-namespace/ JavaScript namespacing with Peter Michaux: http://michaux.ca/articles/javascript-namespacing Modules and namespaces in JavaScript http://www.2ality.com/2011/04/modules-and-namespaces-in-javascript.html

###Custom Events for Pub/Sub (with the Widget factory)

You may have previously used the Observer (Pub/Sub) pattern for developing asynchronous JavaScript applications. The basic idea here is that elements publish event notifications, called topics, when something interesting occurs in your application. Other elements then subscribe/listen out for these topics and react accordingly. This results in the logic for your application being significantly more decoupled (which is always good).

In jQuery, we have this idea of custom events that provide a built-in means for implementing a publish/subscribe system that's quite similar to the Observer pattern. bind('eventType') is functionally equivalent to performing subscribe('eventType') and trigger('eventType') is roughly the equivalent to publish('eventType').

Some developers may consider the jQuery event system as having an unnecessary overhead when used as a publish/subscribe-type system, but it's been architected to be both reliable and robust for most use-cases. In the following jQuery UI widget factory template, we implement a basic custom event based pub/sub pattern that allows your plugin to subscribe to event notifications from the rest of your application, which publishes them.

/*!
 * jQuery custom-events plugin boilerplate
 * Author: DevPatch
 * Further changes: @addyosmani
 * Licensed under the MIT license
 */


/*
Use jQuery’s custom events to enable publish/speak and subscribe/
listen into widgets. Each widget would publish certain events and 
subscribe to others. This approach effectively decouples the widgets 
and allows them to function independently.
*/

;(function ( $, window, document, undefined ) {
    $.widget("ao.eventStatus", {
        options: {

        },
        
        _create : function() {
            var self = this;

            //self.element.addClass("my-widget");

            //subscribe to 'myEventStart'
            self.element.bind("myEventStart", function( e ) {
                console.log("event start");
            });

            //subscribe to 'myEventEnd'
            self.element.bind("myEventEnd", function( e ) {
                console.log("event end");
            });

            //unsubscribe to 'myEventStart'
            //self.element.unbind("myEventStart", function(e){
                ///console.log("unsubscribed to this event"); 
            //});
        },

        destroy: function(){
            $.Widget.prototype.destroy.apply( this, arguments );
        },
    });
})( jQuery, window );

//Publishing event notifications
//usage: 
// $(".my-widget").trigger("myEventStart");
// $(".my-widget").trigger("myEventEnd");

####Recommended reading: Communication between jQuery UI widgets: http://www.devpatch.com/2010/03/communication-between-jquery-ui-widgets/ Understanding the Publish/Subscribe Pattern for Greater JavaScript Scalability: http://msdn.microsoft.com/en-us/scriptjunkie/hh201955.aspx Pub/sub crash course/screen cast by Rebecca Murphey: http://blog.rebeccamurphey.com/pubsub-screencast

###Prototypal Inheritance with the DOM to Object bridge pattern

In JavaScript, we don't have the traditional notion of classes present that you may find in other classical programming languages, but what we do have is prototypal inheritance. Prototypal inheritance is where an object inherits from another object and is a concept we can also apply to jQuery plugin development.

Alex Sexton and Scott Gonzalez have looked at this topic in greater detail before and a summary of their ideas could be noted as the following: for organized modular development it can be of benefit to make a clear separation between the object defining the logic for a plugin and the plugin generation process itself. The benefit of this is that it's both easier to test your plugin code but you can also adjust the way things work behind the scenes without altering how any object APIs you implement may be used.

In Sexton's previous post on this topic, he implements a bridge that allows attaching your general logic to a particular plugin which is implemented in the below template. One of the other advantages this pattern offers is that you don't have to constantly repeat the same plugin initialization code, ensuring that the concepts behind DRY (Don't Repeat Yourself) development are maintained. Some developers may also view this pattern as more easy to read than others.

/*!
 * jQuery prototypal inheritance plugin boilerplate
 * Author: Alex Sexton, Scott Gonzalez
 * Further changes: @addyosmani
 * Licensed under the MIT license
 */


// myObject - an object representing a concept you wish to model (eg. a car)
var myObject = {
  init: function( options, elem ) {
    // Mix in the passed in options with the default options
    this.options = $.extend( {}, this.options, options );

    // Save the element reference, both as a jQuery
    // reference and a normal reference
    this.elem  = elem;
    this.$elem = $(elem);

    // Build the dom initial structure
    this._build();

    // return this so we can chain/use the bridge with less code.
    return this;
  },
  options: {
    name: "No name"
  },
  _build: function(){
    //this.$elem.html('<h1>'+this.options.name+'</h1>');
  },
  myMethod: function( msg ){
    // You have direct access to the associated and cached jQuery element
    // this.$elem.append('<p>'+msg+'</p>');
  }
};


// Object.create support test and fallback for browsers without it
if ( typeof Object.create !== 'function' ) {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}


// Create a plugin based on a defined object
$.plugin = function( name, object ) {
  $.fn[name] = function( options ) {
    return this.each(function() {
      if ( ! $.data( this, name ) ) {
        $.data( this, name, Object.create(object).init( options, this ) );
      }
    });
  };
};

// Usage:
// With myObject, we could now essentially do this:
// $.plugin('myobj', myObject);

// and at this point we could do the following
// $('#elem').myobj({name: "John"});
// var inst = $('#elem').data('myobj');
// inst.myMethod('I am a method');

####Further reading: Using Inheritance Patterns To Organize Large jQuery Applications : http://alexsexton.com/?p=51. Some further discussion: http://www.slideshare.net/SlexAxton/how-to-manage-large-jquery-apps Practical example of the need for prototypal inheritance: http://blog.bigbinary.com/2010/03/12/pratical-example-of-need-for-prototypal-inheritance.html Crockfords Prototypal Inheritance in JavaScript: http://javascript.crockford.com/prototypal.html

###jQuery UI Widget Factory Bridge

If you liked the idea of generating plugins based on objects from the last design pattern, you might be interested in a method found in the jQuery UI widget factory called $.widget.bridge. This bridge basically acts as a middle layer between a JavaScript object created using $.widget and jQuery's API, providing a more built-in solution to achieving object-based plugin definition. Effectively, you're able to create stateful plugins using a custom constructor.

In addition to the above, the $.widget.bridge provides access to a number of other capabilities including the following:

  • Both public and private methods are handled as one would expect in classical OOP (public methods are exposed whilst calls to private methods are not possible)
  • Automatic protection against multiple initializations
  • Automatic generation of instances of a passed object and storage of this within the a selections internal $.data cache
  • Options can be altered post initialization

For further information on how to use this pattern, take a look at the comments in the boilerplate below:

/*!
 * jQuery UI Widget factory "bridge" plugin boilerplate
 * Author: @erichynds
 * Further changes, additional comments: @addyosmani
 * Licensed under the MIT license
 */



// a "widgetName" object constructor
// required: this must accept two arguments,
// options: an object of configuration options
// element: the DOM element the instance was created on
var widgetName = function( options, element ){
  this.name = "myWidgetName";
  this.options = options;
  this.element = element;
  this._init();
}


// the "widgetName" prototype
widgetName.prototype = {
    
    // _create will automatically run the first time this widget is called
    _create: function(){
        // creation code
    },

    // required: initialization logic for the plugin goes into _init
    // This fires when your instance is first created and when 
    // attempting to initialize the widget again (by the bridge)
    // after it has already been initialized.
    _init: function(){
        // init code
    },

    // required: objects to be used with the bridge must contain an 
    // 'option'. Post-initialization, logic for changing options go
    // here. 
    option: function( key, value ){
        
        // optional: get/change options post initialization
        // ignore if you don't require them.
        
        // signature: $('#foo').bar({ cool:false });
        if( $.isPlainObject( key ) ){
            this.options = $.extend( true, this.options, key );
        
        // signature: $('#foo').option('cool'); - getter
        } else if ( key && typeof value === "undefined" ){
            return this.options[ key ];
            
        // signature: $('#foo').bar('option', 'baz', false);
        } else {
            this.options[ key ] = value;
        }
        
        // required: option must return the current instance. When re-
        // initializing an instance on elements, option is called first
        // and is then chained to the _init method.
        return this;  
    },

    // notice no underscore is used for public methods
    publicFunction: function(){ 
        console.log('public function');
    },

    // underscores are used for private methods
    _privateFunction: function(){ 
        console.log('private function');
    }
};

/*
usage:

// connect the widget obj to jQuery's API under the "foo" namespace
$.widget.bridge("foo", widgetName);

// create an instance of the widget for use
var instance = $("#elem").foo({
     baz: true
});

 // your widget instance exists in the elem's data
instance.data("foo").element; // => #elem element

// bridge allows you to call public methods...
instance.foo("publicFunction"); // => "public method"

// bridge prevents calls to internal methods
instance.foo("_privateFunction"); // => #elem element
*/



####Further reading: Using $.widget.bridge outside of the Widget Factory: http://erichynds.com/jquery/using-jquery-ui-widget-factory-bridge/

jQuery Mobile widgets with the Widget factory

jQuery mobile is a framework that encourages the design of ubiquitous web applications that work on both popular mobile devices and platforms as well as on the desktop. Rather than writing unique applications for each device or OS, you simply write your code once and it should ideally run on many of the A, B and C-grade browsers that are out there at the moment.

The fundamentals behind jQuery mobile can also be applied to plugin and widget development, as seen in some of the core jQuery mobile widgets used as part of the official library suite. Whats interesting here is that even though there are very small, subtle differences in writing a 'mobile'-optimized widget, if you're familiar with using the jQuery UI Widget factory, you should be able to start writing these straight away.

The mobile-optimized widget below has a number of interesting differences over the standard UI widget pattern from earlier:

  • `$.mobile.widget` is referenced as an existing widget prototype to inherit from. For standard widgets it's unnecessary to pass through any such prototype for basic development, however using this jQuery-mobile specific widget prototype provides internal access to further 'option' formatting.
  • You'll notice in `_create()`, there's a guide as to how the official jQuery mobile widgets approach element selection, opting for a role-based approach that fits the jQM mark-up more appropriately. This isn't at all to say standard selection isn't recommended, just that this approach may make more sense given the structure of jQM pages.
  • Guidelines are also provided in comment-form regarding applying your plugin methods on `pagecreate` as well as selection for plugin application via `data-roles` and `data-attributes`.
/*!
 * (jQuery mobile) jQuery UI Widget-factory plugin boilerplate (for 1.8/9+)
 * Author: @scottjehl
 * Further changes: @addyosmani
 * Licensed under the MIT license
 */

;(function ( $, window, document, undefined ) {

    //define a widget under a namespace of your choice
    //here 'mobile' has been used in the first parameter
    $.widget( "mobile.widgetName", $.mobile.widget, {

        //Options to be used as defaults
        options: {
            foo: true,
            bar: false
        },

        _create: function() {

            // _create will automatically run the first time this widget is called
            // Put the initial widget setup code here, then you can access the element 
            // on which the widget was called via this.element
            // The options defined above can be accessed via this.options

            //var m = this.element,
            //p = m.parents(":jqmData(role='page')"),
            //c = p.find(":jqmData(role='content')")
        },

        // Private methods/props start with underscores
        _dosomething: function(){ ... },

        // Public methods like these below can can be called externally: 
        // $("#myelem").foo( "enable", arguments );

        enable: function() { ... },

        //Destroy an instantiated plugin and clean-up modifications the widget has made to the DOM
        destroy: function () {
            //this.element.removeStuff();
            // For UI 1.8, destroy must be invoked from the base widget
            $.Widget.prototype.destroy.call(this);
            // For UI 1.9, define _destroy instead and don't worry about calling the base widget
        },

        methodB: function ( event ) {
            //_trigger dispatches callbacks the plugin user can subscribe to
            //signature: _trigger(type, event, objectOfKeyValuePairsToPassToCallback)
            this._trigger('methodA', event, {
                key: value
            });
        },

        methodA: function ( event ) {
            this._trigger('dataChanged', event, {
                key: value
            });
        },

        //Respond to any changes the user makes to the option method
        _setOption: function ( key, value ) {
            switch (key) {
            case "someValue":
                //this.options.someValue = doSomethingWith( value );
                break;
            default:
                //this.options[ key ] = value;
                break;
            }

            // For UI 1.8, _setOption must be manually invoked from the base widget
            $.Widget.prototype._setOption.apply(this, arguments);
            // For UI 1.9 the _super method can be used instead
            //this._super( "_setOption", key, value );
        }
    });

})( jQuery, window, document );

//usage: $("#myelem").foo( options );


/* Some additional notes - delete this section before using the boilerplate.

 We can also self-init this widget whenever a new page in jQuery Mobile is created.
 jQuery Mobile's "page" plugin dispatches a "create" event
 when a jQuery Mobile page (found via data-role=page attr) is first initialized.

 We can listen for that event (called "pagecreate" ) and run our plugin automatically whenever a new page is created.

$( document ).bind( "pagecreate", function( e ){
    // In here, e.target refers to the page that was created (it's the target of the pagecreate event)
    // So, we can simply find elements in this page that match a selector of our choosing, and call our plugin on them.

    // Here's how we'd call our "foo" plugin on any element with a data-role attribute of "foo":
    $( e.target ).find( "[data-role='foo']" ).foo( options );

    // Or, better yet, let's write the selector accounting for the configurable data-attribute namespace
    $( e.target ).find( ":jqmData(role='foo')" ).foo( options );
});

 That's it. Now you can simply reference the script containing your widget and 
 pagecreate binding in a page running jQuery Mobile site and it will automatically run like any other jQM plugin
 */

###RequireJS jQueryUI Widget Factory

RequireJS is a script loader that provides a clean solution for encapsulating application logic inside of manageable modules. It's able to load modules in the correct order (through its order plugin), simplifies the process of combining scripts via its excellent optimizer and also provides the means to define module dependencies on a per-module basis.

James Burke has previously written a comprehensive set of tutorials around getting started with RequireJS, but what if you're already familiar with it and would like to wrap your jQuery UI widgets or plugins inside of a RequireJS-compatible module wrapper?

In the following boilerplate pattern we demonstrate how a compatible widget can be defined which:

  • Allows the definition of widget module dependencies, building on top of the previous jQueryUI boilerplate presented earlier
  • Demonstrates one approach to passing in HTML template assets for creating templated widgets with jQuery (in conjunction with the jQuery tmpl plugin - view the comments in `_create()`)
  • Includes a quick tip regarding adjustments you can make to your widget module if you wish to later pass it through the RequireJS optimizer
/*!
 * jQuery UI Widget + RequireJS module boilerplate (for 1.8/9+)
 * Authors: @jrburke, @addyosmani
 * Licensed under the MIT license
 */

 /*
Note from James:

This assumes you are using the RequireJS+jQuery file, and that the following 
files are all in the same directory: 

- require-jquery.js 
- jquery-ui.custom.min.js (custom jQueryUI build with widget factory) 
- templates/ 
    - asset.html 
- ao.myWidget.js 

Then you can construct the widget like so: 
*/


//ao.myWidget.js file: 
define("ao.myWidget", ["jquery", "text!templates/asset.html", "jquery-ui.custom.min","jquery.tmpl"], function ($, assetHtml) {

    //define your widget under a namespace of your choice
    //'ao' is used here as a demonstration 
    $.widget( "ao.myWidget", { 

        //Options to be used as defaults
        options: {}, 

        //Setup widget (eg. element creation, apply theming, bind events etc.)
        _create: function () {

            // _create will automatically run the first time this widget is called
            // Put the initial widget setup code here, then you can access the element 
            // on which the widget was called via this.element
            // The options defined above can be accessed via this.options

            //this.element.addStuff();
            //this.element.addStuff();
            //this.element.tmpl(assetHtml).appendTo(this.content); 
        },

        //Destroy an instantiated plugin and clean-up modifications the widget has made to the DOM
        destroy: function () {
            //this.element.removeStuff();
            // For UI 1.8, destroy must be invoked from the base widget
            $.Widget.prototype.destroy.call( this );
            // For UI 1.9, define _destroy instead and don't worry about calling the base widget
        },

        methodB: function ( event ) {
            //_trigger dispatches callbacks the plugin user can subscribe to
            //signature: _trigger(type, event, objectOfKeyValuePairsToPassToCallback)
            this._trigger('methodA', event, {
                key: value
            });
        },

        methodA: function ( event ) {
            this._trigger('dataChanged', event, {
                key: value
            });
        },

        //Respond to any changes the user makes to the option method
        _setOption: function ( key, value ) {
            switch (key) {
            case "someValue":
                //this.options.someValue = doSomethingWith( value );
                break;
            default:
                //this.options[ key ] = value;
                break;
            }

            // For UI 1.8, _setOption must be manually invoked from the base widget
            $.Widget.prototype._setOption.apply( this, arguments );
            // For UI 1.9 the _super method can be used instead
            //this._super( "_setOption", key, value );
        }

        //somewhere assetHtml would be used for templating, depending on your choice.
    }); 
}); 

/*
If you are going to use the RequireJS optimizer to combine files  together, you can 
leave off the "ao.myWidget" argument to define: 
define(["jquery", "text!templates/asset.html", "jquery-ui.custom.min"], ..... 
*/


####Further reading: Using RequireJS with jQuery: http://jqfundamentals.com/book/index.html#example-10.5 Fast modular code with jQuery and RequireJS: http://speakerrate.com/talks/2983-fast-modular-code-with-jquery-and-requirejs jQuery's Best Friends: http://jquerysbestfriends.com/#slide1 Managing dependencies with RequireJS: http://www.angrycoding.com/2011/09/managing-dependencies-with-requirejs.html

###Globally and per-call overridable options (Best options pattern)

For our next pattern, we're going to take a look at an optimal approach to configuring options and defaults for your plugin. The way you're probably familiar with defining plugin options is passing through an object literal of defaults to $.extend, as demonstrated in our basic plugin boilerplate.

If however, you're working with a plugin with a large number of customizable options which you would like users to be able to override either globally on a per-call level, you can structure things a little differently to achieve this.

Instead, by referring to an options object defined within the plugin namespace explicitly (eg. $fn.pluginName.options) and merging this with any options passed through to the plugin when it is initially invoked, users have the option of either passing options through during plugin initialization or overriding options outside of the plugin (as demonstrated).

/*!
 * jQuery 'best options' plugin boilerplate
 * Author: @cowboy
 * Further changes: @addyosmani
 * Licensed under the MIT license
 */


;(function ( $, window, document, undefined ) {

    $.fn.pluginName = function ( options ) {

        // Here's a 'best' approach for overriding 'defaults' with specified options. 
        // Note how rather than a regular defaults object being passed as the second
        // parameter, we instead refer to $.fn.pluginName.options explicitly, merging it
        // with the options passed directly to the plugin. This allows us to override
        // options both globally and on a per-call level. 

        options = $.extend( {}, $.fn.pluginName.options, options );

        return this.each(function () {

            var elem = $(this);

        });
    };

    // Globally overriding options
    // Here are our publicly accessible default plugin options that are available in case
    // the user doesn't pass in all of the values expected. The user is provided a default
    // experience but can also override the values as necessary.
    // eg. $fn.pluginName.key ='otherval';

    $.fn.pluginName.options = {

        key: "value",
        myMethod: function ( elem, param ) {
            
        }
    };
    
})( jQuery, window );

####Further reading: jQuery Pluginization: http://benalman.com/talks/jquery-pluginization.html and accompanying gist: https://gist.github.com/472783/e8bf47340413129a8abe5fac55c83336efb5d4e1

###Highly-configurable and mutable plugin

Similar to Alex Sexton's pattern, the plugin logic for our plugin isn't nested with a jQuery plugin itself. We instead define our plugin logic using a constructor and an object literal defined on its prototype, using jQuery for the actual instantiation of the Plugin object.

Customization is taken to the next level through employing two different little tricks, one of which you've seen in previous patterns:

  • Options can be overridden both globally and per collection of elements
  • Options can be customized on a *per element* level through HTML5 data attributes (as shown below). This facilitates plugin behavior that can be applied to a collection of elements, but then customized inline without the need to instantiate each element with a different default value.

The latter is an option you don't see in the wild too often, but it can be a significantly cleaner solution (as long as you don't mind the inline approach). If you're wondering where this may be useful, imagine writing a draggable plugin for a large set of elements. You could go about customizing their options like this..

$('.item-a').draggable({'defaultPosition':'top-left'});
$('.item-b').draggable({'defaultPosition':'bottom-right'});
$('.item-c').draggable({'defaultPosition':'bottom-left'});
//etc

but using this pattern's inline approach would instead support the following being possible:

$('.items').draggable();
<li class="item" data-plugin-options='{"defaultPosition":"top-left"}'></div>
<li class="item" data-plugin-options='{"defaultPosition":"bottom-left"}'></div>

and so on. You may well have a preference over which of these approaches is better, but it's just another potentially useful pattern to be aware of.

/*
 * 'Highly configurable' mutable plugin boilerplate
 * Author: @markdalgleish
 * Further changes, comments: @addyosmani
 * Licensed under the MIT license
 */


// Note that with this pattern, as per Sexton's, the plugin logic
// hasn't been nested inside a jQuery plugin. Instead, we just use
// jQuery for the instantiation of it.

;(function( $, window, document, undefined ){

  // our plugin constructor
  var Plugin = function( elem, options ){
      this.elem = elem;
      this.$elem = $(elem);
      this.options = options;

      // this next line takes advantage of HTML5 data attributes
      // to support customization with the plugin on a per-element
      // basis. eg
      // <div class=item' data-plugin-options='{"message":"Goodbye World!"}'></div>
      this.metadata = this.$elem.data( 'plugin-options' );
    };

  // the plugin prototype
  Plugin.prototype = {
    defaults: {
      message: 'Hello world!'
    },

    init: function() {
      // Introduce defaults that can be extended either globally or using an 
      // an object literal. 
      this.config = $.extend({}, this.defaults, this.options, this.metadata);

      // Sample usage:
      // Set the message per instance:
      // $('#elem').plugin({ message: 'Goodbye World!'});
      // or
      // var p = new Plugin(document.getElementById('elem'), { message: 'Goodbye World!'}).init()
      // or, set the global default message:
      // Plugin.defaults.message = 'Goodbye World!'

      this.sampleMethod();
      return this;
    },

    sampleMethod: function() {
      // eg. show the currently configured message
      // console.log(this.config.message);
    }
  }

  Plugin.defaults = Plugin.prototype.defaults;

  $.fn.plugin = function(options) {
    return this.each(function() {
      new Plugin(this, options).init();
    });
  };

  //optional: window.Plugin = Plugin;

})( jQuery, window );

####Further reading: Creating Highly Configurable jQuery Plugins: http://markdalgleish.com/2011/05/creating-highly-configurable-jquery-plugins/ Highly configurable jQuery plugins - part 2: http://markdalgleish.com/2011/09/html5data-creating-highly-configurable-jquery-plugins-part-2/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment