jQuery UI’s Widget Factory for Stateful Plugins

The ability to create widgets using jQuery UI’s widget factory is incredibly useful, especially when your project’s front-end code is highly dependent on jQuery. The jQuery UI Widget Factory is a component of the jQuery UI library that provides an easy, object oriented way to create stateful jQuery plugins.

Stateful plugins keep track of their current state, maintain state once they have been initialized. In order, to create a consistent API across these plugins, widget factory comes into picture.

In this article, let’s create a simple navigation bar plugin using widget factory API.

Navigation Bar widget

We all know that having easy-to-use navigation is important for any web site. We are going to build a vertical navigation bar which displays and hides via external control. Additionally, we will add a bit of flexibility to the plugin API to control the position of navigation bar. Sounds simple, right? Let’s explore how it can be done using widget factory API.

screen-shot-2017-02-23-at-11-45-16

HTML Markup

The plugin requires an element for the initialization. The content inside this element is not used by this plugin. For this article, some elements have been provided which will be used as menu items. The HTML for navigation bar widget is as follows:

< div id="navigation"> 
< div> Home< /div>  
< div> Blog< /div>  
< div> About< /div>  
< div> Contact< /div>  
< /div> 

Let’s add two buttons to the markup, which will be used for displaying and hiding of this navigation.

< div class="btnContainer">  
< button class="button" id="showNav"> Show Navigation< /button>  
< button class="button" id="hideNav"> Hide Navigation< /button>  
< /div> 

Widget Creation

First, let’s put our script in a self-executing anonymous function that will provide a closure where the code can safely run.

(function($) {
// Our widget API goes here
}(jQuery));

Now add a line of code to create a plugin instance.

$.widget('ts.navbar', {});

The plugin name has a namespace ts which makes it clear where it came from and if it belongs to a large collection or an organization. Here, the namespace ts represents techshard. Similarly, the namespace ui is reserved for official jQuery UI plugins. You must specify your own namespace for your plugins. The name of the plugin navbar is followed by a dot operator.

The widget factory provides some methods by default and they should be overridden to provide functionalities to the plugin. The basic stub for this plugin looks as follows.

$.widget('ts.navbar', {
options: {},
_create: function () {},
_destroy: function () {},
_setOption: function (key, value) {}
});

The navbar plugin can be initialized as follows:

$("#navigation").navbar();

Initializing DOM with _create and options

options: {
/**
navbar will be positioned to left of the window by default
*/
leftPos: true
},
_create: function() {
this.element.addClass('ts-navbar').attr({role:'navbar'});
this.element.children().addClass('ts-menu-item');
this.element.hide();
}

When the plugin gets called, it will create a new plugin instance and all functions will be executed within the context of that instance. this.element is a jQuery object containing exactly one element and this is used as a context throughout the API.

In _create(), we are adding some attributes to the element and its descendants. When the plugin is instantiated, we are hiding navbar, so that we can display on clicking the button later.

Note that _ before a method name is used for only private methods in the API.

The options is a hash containing key/value pairs for all of our plugin’s options. This is accessed using this.options throughout the API. The option leftPos in this widget determines if the navbar should be positioned to the left or right. The value of the option can be accessed using this.options.leftPos within the widget and $("#navigation").navbar(‘options’, ‘leftPos’) outside the widget.

The navbar will be positioned to left by default, if we do not override options in our plugin constructor. If we want to it to be on the right, we can initialize the plugin as follows:

Plugins created using the Widget Factory should be able to have their options updated at any time and should respond accordingly to the changes. To facilitate this, the Widget Factory provides a _setOption(), which you should override when implementing your widget.

/**
* @private
*/
_setOption: function(key, value) {
if (key === 'leftPos') {
this.options.leftPos = value;
}
// base
this._super( key, value );
}

this._super() invokes the method of the same name from the parent widget, with any specified arguments.

_setOptions() allows us to set options by calling this._setOption(key,value) internally and by calling either $(selector).navbar("option","key", value) or $(selector).navbar("option", { key: value }) from outside the plugin. In this plugin, the position of the navbar can be modified as follows:

$("#navigation").navbar('option', 'leftPos', false);

Custom Functions

So far, we have initialized DOM and options, let’s explore how to display and hide navbar by writing some custom functions. These custom functions will be called from outside of our plugin.

/**
* @public
*/
show: function() {
this.element.css('float',
this.options.leftPos ? $.ts.navbar.float.left : $.ts.navbar.float.right);
this.options.leftPos ? this.element.css('left', '0') : this.element.css('right', '0');
this.element.show();
}
/**
* @public
*/
hide: function() {
this.element.hide();
}

show() is just setting some css attributes to the element depending on the value of option leftPos. These functions can be called through this.show() and this.hide() internally and $("#navigation").navbar('show') and $("#navigation").navbar(‘hide’) from outside our widget. I have used some constants $.ts.navbar.float.left and $.ts.navbar.float.right in show(). These are defined using $.extend() within the anonymous function (function($){ }(jQuery)). I usually add property version to the object using this function. In this plugin, I have added and additional property to be used as constants.

/**
* Constants
*/
$.extend($.ts.navbar, {
version: '1.0.0',
float: { right: 'right', left: 'left' }
});

Cleaning Up

In some cases, it may become necessary to clean up all the stuff you do in the plugin API, when the plugin is removed. This can be accomplished via _destroy(). The _destroy() for navbar plugin is as follows:

/**
* Removes the navbar functionality completely. This will return the
* element back to its pre-init state.
* @private
*/
_destroy: function() {
this.element.children().removeClass('ts-menu-item');
this.element.removeClass('ts-navbar').removeAttr('role');
}

Here, I am just removing class and role attributes from the element and its descendants, which were defined in _create(). You may also want to unbind events in _destroy() in your plugins. This method can be used for garbage collection as well. It can called via $("#navigation").navbar(destroy) from outside the plugin, which will automatically call _destroy() of the navbar plugin.

Wrapping Up

I have been using widget factory extensively in my projects for creating stateful plugins and for code reuse. The source code for this plugin can be found here.

If you would like to explore further, refer jQueryUI Widget Factory Official documentation. I hope you will find it useful too.