The jQuery UI Widget Factory

Contact Us or call 1-877-932-8228
The jQuery UI Widget Factory

The jQuery UI Widget Factory

While there are a variety of approaches one can take when authoring plugins, we will here use the jQuery UI Widget Factory strategy. Recommended by jQuery Mobile, the Widget Factory "provides a flexible base for building complex, stateful plugins with a consistent API". See the jQuery Development & Planning Wiki for more info.

Let's look at how plugins are written by checking out an example. Suppose that some content for Nan & Bob's site comes from an external source - an XML syndication, say, or a database-backed CMS. Lots of the content happens to be images; the markup we inherit is a series of img tags wrapped in a div:

<div class="imgs">
<img src="images/book1.jpg" title="Book 1" alt="Demoveo eu inhibeo immitto in nulla quae. Haero facilisi valetudo nulla plaga, facilisis. ">
<img src="images/book2.jpg" title="Book 2" alt="In, augue eu utinam ut vulpes feugiat nulla autem damnum esca in melior.">
<img src="images/book3.jpg" title="Book 3" alt="Te diam tum distineo iriure roto, mara. In indoles enim iaceo abico zelus mauris vicis indoles quis. Wisi scisco.">
<img src="images/book4.jpg" title="Book 4" alt="Vel singularis vel, vicis capio importunus eum vel suscipit oppeto nisl ut iustum indoles nullus.">
</div>

It would be nice to present this inherited content in a manner both prettier and more useful for mobile visitors. We'll write a plugin to display these sets of images in a collapsible grid. The screenshot below shows (on the left) a demo page without applying the plugin, which we'll call "Collapsify", and the result (on the right) of applying the plugin. Note that the label for the collapsible header draws from each image's title attribute and that the text content inside the collapsible element draws from each image's alt attribute.

Collapsible plugin

Open up Plugins/Demos/index.html to see the plugin in action. Note that the page links the JavaScript file jquery.webucator.collapsify.js - this is where we have written our plugin code.

On pagecreate, we invoke the plugin:

$(document).on('pagecreate',function(){
$('div.imgs').collapsify({'datatheme':'e', 'openindex':0});
});

We are invoking the collapsify plugin on all divs of class imgs and passing a hash with two parameter name/value pairs: datatheme has value b and openindex has value 0.

The interesting stuff is in the JavaScript code that defines the plugin itself - open up Plugins/Demos/jquery.webucator.collapsify.js in a file editor to check it out.

(function( $ ){
$.widget("webucator.collapsify", $.mobile.widget, {
options: {
datatheme: 'a',
openindex: null
},
_create: function() {
this.element.attr('data-role','collapsible-set').attr('data-theme',this.options.datatheme);
this.images = this.element.children();
var openindex = this.options.openindex;
this.images.each(function(index) {
$(this).wrap('<div data-role="collapsible"' + (openindex == index ? ' data-collapsed="false"' : '') + ' />').before('<h2>' + $(this).attr('title') +'</h2>').after('<p>' + $(this).attr('alt') + '</p>');
});
this.element.enhanceWithin();
},
_setOption: function(key, value) {
this.options[key] = value;
this._super( "_setOption", key, value );
},
});
})( jQuery );

We wrap the code in function ( $ ) - this adds our plugin to the jQuery object.

Next, we define our plugin. webucator.collapsify defines both a namespace (webucator, to avoid collisions with others' plugins) and the name (collapsify) we give to our plugin. The next parameter, $.mobile.widget, states that we are subclassing widget. The following opening brace ({) is the start of an object litteral that defines the contents of our plugin.

The options field is a hash defining parameters to which we give default values; the user can override these values by supplying a hash when calling the plugin. Method _setOptions - a private method, because of the underscore at the start of its name - captures the option values, if any, supplied by the user. Note that we are overriding (and calling) the parent version of this method; often, we would want to add some logic in this method - validating some parameters, say, to conform to logical values.

Method _create is the constructor for our plugin. The first line adds two attributes to the div upon which the widget is called: we add data-role="collapsible-set" to make this a collapsible set, and we add the data-theme attribute to use the value from the datatheme option specified when initializing the plugin.

this.element refers to the object upon which the plugin is acting. Note that, in the context of the plugin, this refers to the object upon which the plugin is operating - not the DOM element. Thus we write code like this.element.css('padding'), rather than $(this).element.css('padding'). (That changes when we use a jQuery iterator to loop over the set of images: inside the each loop, as we'll see below, $(this) refers to each element of the loop.)

openindex is the option value (defaulting to null) of the item to be open on page load, indexed (like JavaScript arrays) starting at zero, not one.

Next, we loop over the images found in the div upon which our plugin is acting. Here we'll make use of the following jQuery functions:

  • .wrap() wraps an HTML structure around the inner HTML of each element in the set of matched elements.
  • .before() inserts an HTML structure or DOM element before each element in the set of matched elements.
  • .after() inserts an HTML structure or DOM element before after element in the set of matched elements.

We wrap each image in a div, with attribute data-role="collapsible", adding attribute data-collapsed="false" if we happen to be on the element specified to be open. We also add an <h2> and a <p>, the contents for which we get from the title and alt attributes of the image, respectively.

Last, we use this.element.enhanceWithin() to refresh the DOM, to ensure that our changes show for the user, since the page's DOM will have already loaded.

Though it may look fairly complex, our example here is relatively simple: we haven't captured any state and manipulated the DOM just a little bit. Many plugins are extensive - handling state, capturing events, etc. - but this simple example shows the power of plugins.

Next