jQuery UI – Widget factory (v 1.8)

Full disclosure: This is an article that I wrote for semantic-interaction.org and I just copied all information from there.

- or: “How jQuery UI saved me about 300 lines of code!”

During the last few days, I’ve attended the Aloha Editor DevCon which was continued by the IKS Semantic interaction hackathon which was held in Vienna, Austria. To make a long story short, it has been a great week with a lot of high-class programmers from the web field and I learnt a lot about web development and had some great discussions about the usage and benefits of semantic information in the web.

One discussion that I had with Richard Worth from the jQuery UI DevTeam was about how to use the widget factory from jQuery UI to use it in the semantic interaction framework, I was developing during that time. So – returning to the subtitle of this page – using their factory implementation actually saved me 353 lines of code and hence made the code cleaner, more robust and understandable for other developers. That was such an impressing outcome and discussion that I dedicate this post and explain how one can use jQuery UI’s widget factory to his/her own benefit. Most of this information is also available on Nemikor’s blog.

Background

In the VIE^2 framework, I need the functionality to lift jQuery objects to semantic objects. Furthermore, I have to keep track of these elements, take care of their persistence and require global options, local options for each element and function-specific options. Following the jQuery pattern, an easy-adaptable API that supports chaining and a customised event-system where also on my agenda. After around two days of implementation, I finally had most of these functionalities together. However, the persistency of the lifted objects were kind of a worry for me. I then talked to Richard and he volunteered to give me a one hour introduction in the jQuery UI widget factory that actually supported *all* of the required functionalities. (well, excluding the semantic lifting of course ;-) ) And all that within a few lines of code…

Namespacing and binding

In jQuery UI, everything is about namespacing and class attributes. Clearly, we want the separate our example widget from the common ‘ui’ namespace that jQuery UI uses. The following code is actually everything we need for that.

 

(function($, undefined) {
    $.widget('Example.example', {
        _create: function () {
            console.info('hello world!');
        },
    }
})(jQuery);

A few things happen when this code is loaded on your webpage:

  1. A jQuery object is allocated which is accessible via: $.Example.
  2. A jQuery function is registered which enables us to call .example() on every jQuery object (yes, even collections).

Imagine we have the following two elements on our webpage:

<span class="test">This is a test</span>
<span class="test">This is also a test</span>

With the above 7 lines of code, we can now call:

$('.test').example();

What happens? As there are two elements selected by the class selector of jQuery, the ‘_create’ function is called twice (one time for each element) and hence, the output of the console looks like:

'hello world!'
'hello world!'

Function names:

Private functions have a leading _ (underscore) character and (of course) functions can be added and overwritten by desire.

(function($, undefined) {
    $.widget('Example.example', {
        _create: function () {
            console.info('hello world!');
        },
        //new public function
        doSomething: function (options) {
            if (options) {
                //do something
            }
        },
        // new private function
        _doSomethingElse: function () {
           // do something else
        } 
    }
})(jQuery);

Their big difference is that private functions can not be called on the widget, whereas public functions can be easily called using the provided widget syntax:

$('#id').example('doSomething', <arg1>, <arg2>, <arg3>, ...); // the arguments are passed as array to the function doSomething.

The widget factory comes with the following predefined preivate functions:

_create: called once for each element
_init: called everytime ‘.example()’ is executed on an element.
_setOption(key, value): Sets a value of the options.
_trigger(name, event, options): Triggers the event ‘name’ on this object.

Options

We have access to the options using:

$('#id').example('option', 'x'); //returns the value of 'x' stored in the widget's options.

The jQuery UI widget provides three methods to set options:

1. The global (or default) setter:

(function($, undefined) {
    $.widget('Example.example', {
        options: {
            x: 0
       }
...

This defaults the value ‘x’ to 0.

2. The widget-specific setter:

$('#id').example({x: 15});

3. The ex post setter:

var elem = $('#id').example();
elem.example('option', 'x', 15);

Example

Assuming that we use the default setting like shown above:

var elem = $('#id').example();
console.info(elem.example('option', 'x'));// prints '0' on the console
elem.example('option', 'x', 10);
console.info(elem.example('option', 'x')); // prints '10' on the console
var elem2 = $('#id2').example({'x': 15});
console.info(elem2.example('option', 'x')); // prints '15' on the console

Events

I already indicated the ‘_trigger’ method to execute widget-sepcific events. Well, here’s how this works in more detail:

(function($, undefined) {
...
        options: {
            ready: function () {}
        },
        //new public function
        triggerReady: function (event) {
            this._trigger('ready', event, {key: "Some data"});
        },
...

What we can do now in, e.g., the HTML page is the following:

var elem = $('#id').example();
elem.bind('exampleready',
    function (ev, da) {
        console.info('The event:' + ev);
        console.info('The data:' + da.key);
    }
);
elem.triggerReady("hello world!");
// prints the following two lines on the console:
//     The event:hello world!
//     The data object:Some data

What’s special about this are two things:

  1. The event needs to be “allocated” in the options, marked as functions (that’s why Nemikor also refers to them as ‘callbacks’)
  2. The events have a special “namespace” where the event’s name is headed by the name of the widget (example (lowercase E) in our case).

Prototype

Let’s assume, we want to have a global logging for all our example widgets. Clearly, we do not want to write this code everytime for every widget (memory leaking). Hence, we can write a logger after the initialization:

(function($, undefined) {
    $.widget('Example.example', {
        _create: function () {
            console.info('hello world!');
        },
        ...
    }
})(jQuery);
$.Example.log = function (msg) {
    console.info('INFO(Example): ' + msg);
}

Please consider that we add this to the ‘$.Example’ (capital E) namespace, as this element exists only once in the memory.

Keeping track of all objects:

We can now easily query all elements that are in the ‘Example’ namespace using:

$(':Example-example');

Related links:

  1. jQuery
  2. jQuery UI
  3. Widget factory on Nemikor’s blog

About

Sebastian Germesin » 20-somewhat years old » web-enthusiast » Web-{Design,Develop}er » Sports-fanatic » Blogger

Copyright © Sebastian Germesin's thoughts on
… generally anything that rushes through his mind

Built on Notes Blog Core
Powered by WordPress