Complete Widget Factory Pattern

While the jQuery plug-in authoring guide is a great introduction to plug-in development, it doesn’t help obscure away common plug-in plumbing tasks that we have to deal with on a regular basis.

The jQuery UI Widget Factory is a solution to this problem that helps us build complex, stateful plug-ins based on object-oriented principles. It also eases communication with our plug-ins instance, obfuscating a number of the repetitive tasks that we would have to code when working with basic plug-ins.

Stateful plug-ins help us keep track of their current state, also allowing us to change properties of the plug-in after it has been initialized.

One of the great things about the Widget Factory is that the majority of the jQuery UI library actually uses it as a base for its components. This means that if we’re looking for further guidance on structure beyond this pattern, we won’t have to look beyond the jQuery UI repository on GitHub (https://github.com/jquery/jquery-ui).

This jQuery UI Widget Factory pattern covers almost all of the supported default factory methods, including triggering events. As per the last pattern, comments are included for all of the methods used, and further guidance is given in the inline comments.

/*!
 * 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 our widget under a namespace of your choice
    // with additional parameters e.g. 
    // $.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 (e.g. 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 we 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 plug-in 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 plug-in user 
            // can subscribe to
            // signature: _trigger( "callbackName" , [eventObject], 
            // [uiObject] )
            // e.g. this._trigger( "hover", e /*where e.type == 
            // "mouseenter"*/, { hovered: $(e.target)});
            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:

var collection = $("#elem").widgetName({
  foo: false
});

collection.widgetName("methodB");

Get Learning JavaScript Design Patterns now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.