I was looking for a simple yet complete example of a working widget with widget factory from a scratch. Having trouble finding one, I came up with this blog post. Here I would explain how to create a custom widget from scratch. Widgets or Plugins are simple generic control or a unit of process that can be attached to any DOM element to give it a specialized behavior with methods and properties. The jQuery UI Widget Factory is the base class which is extended by all widgets.

Dependencies

To build a simple jQuery widget you need jQuery core library and jQuery UI library. JQuery UI is what contains the definition and base classes for widget factory. You may customize it to download the bare minimum to make your widget work. These can be downloaded from jQuery website or referenced as a link to CDN (Content Delivery Network).
JQuery core library can be downloaded from here
JQuery UI can be downloaded from here
Microsoft Ajax CDN contains the links for most jQuery core and jQuery UI that can be referenced. Visit http://www.asp.net/ajax/cdn for more details.

Project Structure

As a good practice for JavaScript coding, there are few things you should follow:
  • Keep jQuery core library files and your custom code files in separate folders
  • Use small case or camel case naming rules
  • Use uncompressed for development/debugging and compressed for production
  • Use namespace that uniquely separates your widget from other jQuery UI widgets
Here is a picture for project structure, I have created:

Widget Code Structure

There are plenty of materials out on the web explaining how to write a widget using widget factory. To kick start here are few good ones:You might come across multiple ways of doing same thing. This is because JavaScript itself is quite versatile and does not impose restrictions like most compiled languages do. However, as we move forward, I will explain the important methods that would get you going and start writing some interesting widgets. Following image explains the structure of a simple widget that uses widget factory.

Note the followings:
  • (function ($) { ... }(jQuery)); - This implies anonymous function to which jQuery is passed as an argument.
  • $.widget - This is where it calls and extends the base class of jQuery UI widget factory.
  • my.roundButton - This is the fully qualified namespace and class name of my widget.
  • Method prefixed with underscore are meant to be private. This is just a convention, since, there is no scope identifier in JavaScript.
  • options: This defines all the widget properties with default values.
  • _create: This is like a constructor. Fired only once by the widget factory during initialization.
  • _init (not in my code): This is also like a constructor. Fired every time by the widget factory each time widget is called without arguments. Mainly, used for default functionality implementation.
  • _setOption: Called to change a single property value of the widget defined in options. Example - $('#elem').roundButton('option', 'size', 100)
  • _setOptions: Called to change a multiple property value of the widget defined in options.
  • _destroy: This is like a Destructor. Cleanup code goes here.

Instantiation

Let us look at the html page that uses this widget. Things to note here is what scripts are referenced and how a widget is initialized with multiple arguments.

Note how multiple properties are set during the instantiation of the widget. Also note how a widget method is called with parameters. We will discuss about the function attached to "OnPressed" property in the next section.

Custom Events

If you have authored any control in any language, you must have come across the need of raising or chaining events to allow user implement custom code during state changes of the control. For example a user wants to execute some custom action when the widget is clicked. It might be a timeout, ajax call or an alert. You, as an author need to raise an event when the widget is clicked and let the user handle the custom action.

This can be done using the "_trigger" function of jQuery core. Signature of this function is : _trigger("callbackFunction", [eventObject], [arguments]). One must also check if the user has provided custom action code. In other words if the callbackFunction is not null then call it using "_trigger" function. For this, we will create a property in options section called "onPressed". During the click event if the "onPressed" is not null, the widget will call the "_trigger" function.

if (this.options.onPressed != null)
    this._trigger("onPressed", this.control);

Complete Code of "roundButton" Widget

(function ($) { $.widget("my.roundButton", { //control properties options: { radius: '5px', text: "Click Me!", backColor: "#bbbbbb", onPressed: null }, //ctor (called only once) _create: function () { //remove and redraw the control this.element.empty(); //save the reference in this.control this.control = $("<div>") .html(this.options.text) .css("border", "2px solid #a1a1a1") .css("padding", "10px 40px;") .css("background", this.options.backColor) .css("width", "300px;") .css("border-radius", this.options.radius) .appendTo(this.element); //create element this._refresh(); //bind events this._bindEvents(); }, _refresh: function () { this.control .css("background", this.options.backColor) .html(this.options.text); }, //single property setter _setOption: function (key, value) { //update widget factory this._super(key, value); }, //multiple property setter _setOptions: function (options) { //update widget factory this._super(options); this._refresh(); }, //bind internal click event that triggers onPressed event _bindEvents: function () { //remove event binding //Notice the use of this.control this._off(this.control, "click"); //rebind this._on(this.control, { "click": function () { //change property this.options.backColor = "#e7e7e7"; this.options.text = "Clicked"; //refresh this._refresh(); //We will learn how to trigger a custom event in jQuery Widget // Signature of _trigger //this._trigger("callbackFunction", [eventObject], [arguments]) if (this.options.onPressed != null) this._trigger("onPressed", this.control); //change property this.options.backColor = "#bbbbbb"; this.options.text = "Click Me!"; //refresh this._refresh(); } }); }, //public method changeText: function(txt){ this.options.text = txt; //refresh this._refresh(); }, //cleanup _destroy: function () { this.element.empty(); } }); }(jQuery));


Hope it helped ... Enjoy !!