Building JQuery Widgets Part 2

In this article, we’re going to look at some of the finer aspects of the plugin we last build a little bit ago.  Let’s start by looking again at the _init method.  As I mentioned previously, this method runs when the plugin is initialized, after the _create method runs (if there is one).  On init, we have this definition.

_init: function() {

this.options = $.extend({

title: “Interactive Help”,

buttonText: “OK”,

alertIfHidden: true,

beforeShowCallback: null,

afterShowCallback: null,

nextCallback: null,

startingCallback: null,

finishCallback: null

}, this.options);    this._elements = [];

this._currentIndex = 0;

this._total = 0;

this._buildUI();

},

Notice the use of the $.extend method; this method is very handy for establishing default values.  The method takes the values passed into the first parameter, and passes it to the second object, only if a value doesn’t already exist.  This is a great way to ensure a field exists, and helps you from having to write a lot of code that checks for a property’s existence.  What I mean by that is if you didn’t use the extend method, if your plugin tries to use this.options.startingCallback, when startingCallback was never supplied in the options, then an startingCallback is undefined an an exception is the result.  Our implementation of extend above ensures a title, buttonText, etc. field exists on the options object, so we don’t have to do that type check.

Notice that we have some options with a “callback” extension; these are the equivalent to an event in .NET or some other object-oriented language.  An event in an object-oriented language is defined explicitly, allowing the consumer of that event to attach to it and receive notification when the event occurs.  Widgets have the same functionality, in a different way.  We can attach to the event handler by supplying a JavaScript function, and then invoke that function at the appropriate time At various points in the plugin, we’ll invoke the callback by doing the following:

if (!!this.options.nextCallback)

this.options.nextCallback(this, { index: this._currentIndex, total: this._total });

We invoke the method, if it exists, and pass in any additional parameters.  Another important point to note is that you as a developer can interact with the consumer through these callbacks.  For instance, take a look at the code below.

var args = $.extend({
oldElement: el,
oldLeft: (!!pos && !!pos.left) ? pos.left : null,
oldTop: (!!pos && !!pos.top) ? pos.top : null,
element: null,
selectorFunction: null,
left: null,
top: null
}, def);if (!!this.options.beforeShowCallback)
this.options.beforeShowCallback(this, args);

if (!!args.element)
{
el = args.element;

if (!el && typeof(args.selectorFunction) !== “undefined”)
el = args.selectorFunction({ element: this.element });
if (!!el) {
pos = this._getElementPosition(el);
}
}

In this example, we construct an event argument that has specific parameters.  These parameters are passed to the beforeShow event, through the beforeShowCallback (notice the use of extend to ensure certain parameters exist).  After it’s called, the widget interrogates the argument object for any changes the user may have made.  For instance, the user may have supplied an element, or even a function for selecting the element.  In this way, users can provide the widget input at the time the event has executed, something we also have the ability to do in object-oriented languages.

When creating widgets that others on your development team, or possibly the world if  you release your widget to the public, the difficulty comes with how to let them know how to use your plugin, what parameters are available for each callback, and other aspects requiring documentation.  Without good documentation, that supply the parameters being passed in at initialization time or supplied by a callback, the consumer of the widget will have difficulty using it.  While we don’t always like documentation, providing it is key to your widget’s success.  Sure, the above average developer will read the source, but the rest of your audience will be left in the dark without good direction.

Advertisements

Building JQuery Widgets Part 1

JQuery provides an extensive reusability system in the form of plugins and widgets.  Plugins are native to JQuery and reside as a function that can be defined either statically, or per a range of elements.  We’ll touch on JQuery plugins in a later post.  JQuery widgets, on the other hand, are  a part of the JQuery UI framework.  At a minimum, from the JQuery UI download page (http://jqueryui.com/download/), all you need to download is the core feature, the widget feature, and the mouse feature (for mouse-oriented widgets).

This now allows us to create a widget as so:

 $.widget(‘ui.HelpBox’, { /* Definition of Widget */ });

JQuery begins a widget by using the $.widget  method (added on by JQuery UI).  This is the point the widget gets defined; the definition of the widget comes in the form of a class definition, as we’ll see soon.  It may be beneficial to see how you would create a widget, to add some perspective.  Below is how we would instantiate our HelpBox widget.

$(“#SomeElement”).HelpBox({ /* options */ });

Here you can see a widget is applied to an element. A widget is linked to an element within the DOM. It is at this point we are actually instantiating our HelpBox widget, and providing the initial settings for the widget (or options).

Jumping back to our widget definition, when its created, the _create and _init methods are invoked.  In most scenarios, the _init method is sufficient.  Below is the body of the widget defined in the “Definition of Widget” section in the comment above, in addition to some other methods.

{

_init: function() { . . },   _buildUI: function() { . .},

add: function(elementSelector, descriptionHTML, name) { . . },

_finish: function() { . . },

getSize: function() { . . },

next: function() { . . },

run: function() { . . },

_show: function(def) { . . },

stop: function() { . . }

}

We are essentially building the API of the widget, much like building an object’s API using the prototype definition. Usually a JavaScript class has additional variables used to track pieces of information pertinent to the class. During the init method, this is where we will do that, as shown  below.

_init: function() {

this.options = $.extend({

title: “Interactive Help”,

buttonText: “OK”,

beforeShowCallback: null,

afterShowCallback: null,

startingCallback: null,

doneCallback: null

}, this.options);

this._elements = [];

this._currentIndex = 0;

this._total = 0;

this._buildUI();

},

During init, we add instance variables to the widget using “this.variable”, which appends a variable to the widget instance. The “this” pointer points to an instance of the widget, which also allows us to call additional method. Notice the use of the extend method? This JQuery method is pretty handy; what it does is compare the first object against the second, and anywhere a member doesn’t exist in the second object, the default value provided in the first object is added to it, and returned from the method. So you can see the first object that’s statically defined is ensuring certain options have been defined by the user. This way, they won’t cause undefined errors later on.

Let’s turn to constructing the user interface:

_buildUI: function() {

var self = this;   self._outerElement = $(“<div/>”).addClass(“HelpBoxWrapper”).css(“display”, “none”);

self._titleElement = $(“<div/>”).addClass(“HelpBoxTitle”).html(“<h3>” + this.options.title + “</h3>”);

self._bodyElement = $(“<div/>”).addClass(“HelpBoxDescription”);

self._buttonRowElement = $(“<div/>”).addClass(“HelpBoxButtons”).append(

$(“<a />”).attr(“href”, “#”).html(this.options.buttonText)

.click(function() {

self.next();

})

);

self._outerElement.append([self._titleElement.get(0), self._bodyElement.get(0), self._buttonRowElement.get(0)]);

$(“body”).append(self._outerElement);
},

At the core of each widget is an element.  This element is referred to by “this.element”, a reference to the JQuery object containing that element.  Here we are creating some DIVs and other elements, applying CSS to the elements, which will be the user interface of the widget.  The user interface has a button, which listens to the click event.  On click of the button, it invokes a next method, a method that performs an action on the widget.  We’ll look at that in the next post.  Notice how we store a reference to each element of the widget as a variable reference (self is a pointer to this, this widget).

Usages of Widgets

Using widgets is a little awkward.  What looks like the same syntax is actually not the same.  Let’s look at how we may initialize the widget again:

$("#SomeElement").HelpBox({ buttonText: "Okay", title: "Help" });

This is the point of initialization. This calls _create and _init. We provided some options, which override the defaults that we specified in the _init method (“Okay” overrides “OK” as the default button text). Now, to call a method (like the run method) using the JQuery widget, the following is the syntax.

$("#SomeElement").HelpBox("run");

We don’t actually call a method on the object; we use the widget instance to invoke the method. It’s a little awkward at first, but eventually you get used to it. To call our add method, with parameters, you simply pass in the parameters like so:

$("#SomeElement").HelpBox("add", "#MyTargetElement", "My target description");

You can see that all interaction with the widget goes through the widget, rather than the widget’s API. You can also override the options by first specifying the “options” collection, then the name of the option, and lastly provide the new value.

I hope this was an informative look at using JQuery Widgets in your applications.