Friday, August 31, 2012

A Quick Preview of Building Widgets with the jQuery UI 1.9 Framework

If you've been following along with the development of jQuery UI 1.9, you'll see there are a lot of changes coming. With the first release candidate out on August 24th, we might be closing in on a final release soon. Many of the existing widgets are being refactored to use the new plumbing being created in the heart of the framework: the widget factory. For developers using the UI library, there's some new widgets, better accessibility support, and localization capabilities. For widget builders, the widget factory has some great new features that makes building widgets a whole lot easier.

The two biggest changes to the factory API is the addition of _super() for inheritance chaining and _on()/_off() for simplified event binding. By adding these two concepts, the frameworks handles a lot of the complexities related to building proxies, fixing scope, and cleaning up references.

In 1.8, you had to do this to call the original destroy function defined in the base object:


destroy: function()
{
$.Widget.prototype.destroy.call(arguments);
}


Now, in 1.9, you only need to call _super():


_destroy: function()
{
this._super();
}


Now, you don't need to know the parent in the inheritance chain, deal with passing arguments, or any of the other messy detail because its all handled in the framework.

The addition of the private _on/_off event helpers is probably one of the features I'm most happy to see. Getting the scope corrected so you had a reference to the widget instance was cumbersome. Plus, you had to keep track of all your bound events and clean them up so you didn't leak memory. In 1.8, you might have done something like this to fix the scope:


_create: function()
{
...
this.element.on('click',
$.proxy(function(e)
{
// "this" refers to the widget instance
// e.currentTarget is the DOM element we bound the event to
// e.target is the actual DOM element clicked
alert(e.currentTarget.id + ' caught click event which is a ' + this.widgetName);
}, this));
...
}


This might not look that bad, but it doesn't take care of unbinding the event when the widget is destroyed. In 1.9, things are much simpler when using _on():


_create: function()
{
...
// this.element is assumed to be the target
// when nothing is passed as the first arg to _on()
this._on({
click: function(e)
{
// "this" refers to the widget instance
// e.currentTarget is the DOM element we bound the event to
// e.target is the actual DOM element clicked
alert(e.currentTarget.id + ' caught click event which is a ' + this.widgetName);
}
});
...
}


Internally, _on() corrects the scope and stores a reference to your binding so when the widget is destroyed, everything is cleaned up. What I really like about this setup is I can organize all my event handlers in one place in the widget definition and bind them all during initialization. It makes for nice, readable code:


_create: function()
{
...
this._on({
click: this._handleClick,
mouseover: this._handleMouseOver,
...
});

// Bind based on a selector
this._on('selector', {
keydown: this._handleTheOtherKey,
...
});
...
}
...

// Define event handlers ...
_handleClick: function(e)
{
alert(e.currentTarget.id + ' caught click event which is a ' + this.widgetName);
},
... more handlers ...



Other convenience features included by default in widgets created from the framework include a _delay() method which will call a function after a specified timeout with the correct scope. This method is then used in the base object to add default fade in/out effects for show and hide.

Additionally, widgets can call _hoverable() and _focusable() the identify which elements should have this behavior applied. The framework will automatically add/remove the UI theme classes to these elements.

I'm greatly anticipating the final release of the 1.9 UI library. I've already started using it in my development to help find issues and start taking advantage of all the new features.