jQuery UI Custom Control - Widget Factory
Written by Ian Elliot   
Wednesday, 17 April 2013
Article Index
jQuery UI Custom Control - Widget Factory
Method and Events

So you have been using jQuery and jQuery UI but you have a few custom JavaScript controls that don't work in the same way. The solution is to create a jQuery UI custom control so that you can use everything in a uniform way. The only problem is that you have to use the mysterious Widget Factory, which makes it seem more difficult than adding to basic jQuery. 

 Available as a Book:

smallcoverjQuery

buy from Amazon

  1. Understanding jQuery
  2. Basic jQuery CSS Selectors
       Extract: The DOM
  3. More Selectors
       Extract: Basic Selectors
  4. The JQuery Object
  5. Filters 
  6. DOM Traversal Filters 
  7. Modifying DOM Objects
       Extract: Modifying The DOM 
  8. Creating Objects & Modifying The DOM Hierarchy
  9. Working With Data
       Extract: Data ***NEW!!!
  10. Forms 
  11. Function Queues
  12. Animation 
  13. jQuery UI
  14. jQuery UI Custom Control
  15. Easy Plugins 
  16. Testing With QUnit
  17. Epilog A Bonus Function

Also Available:

jquery2cover

buy from Amazon

A Simple Custom Control

First we need a very simple custom control to use and an example. As it really doesn't change anything very much if the custom control is simple or complicated let's opt for the simplest possible. Our custom control will just be a button with a preset label. In standard JavaScript using jQuery this would be created using something like: 

var button=$("<button>");
button.text("My Experimental Button");
$('div').append(button);

The steps are fairly general:

  1. First create the DOM objects you need to represent your custom control. In this case there is just one DOM object and it is wrapped in a jQuery object to make working with it easier.

  2. Second customize the DOM objects that make up your control. In this case we simply change the button's inner HTML to the caption we want.

  3. Finally add the custom object to some target element in the page DOM. In this case the button is added to the be the next child of a div element but more usually this would be a div or some other element with a given id or class. 

To appreciate the logic of adding a custom control to jQuery UI you have to pretend that our custom control is a lot more complex than this simple example with lots of DOM objects and lots of functionality built into it and probably implemented as a function that creates the control.

The main thing to realize is that a custom control added to jQuery UI has to work in the same way as the other controls. If you were to have to implement all of this functionality from scratch there would be a lot of code to write.

The good news is that jQuery UI provides a Widget factory that will take an object that defines your custom control and wrap it with a lot of boiler plate code so that it works within jQuery UI. 

The Widget Factory

That is to add your custom control to jQuery UI you first create an object - myWidget - say and then you use this within a call to the widget factory to add your widget to jQuery UI. 

So suppose we have our myWidget object, and there will be much more on what it needs to contain in a minute, then we can add it to jQuery UI using:

$.widget(name,myWidget);

where name is the name that you want the new widget to have. This has to be a two part name with a namespace and the name of the particular widget. This allows you to add a set of widgets to jQuery UI all under a single name space. 

Ok lets try this out with out very simple custom widget. 

First you need to make sure you are loading jQuery and jQuery UI and the simplest thing to do for a demonstration is to use the Content Delivery Network to download them:

<link rel="stylesheet" href=
 "http://code.jquery.com/ui/1.10.0/themes
       /base/jquery-ui.css" type="text/css"/> 
<script src=
   "http://code.jquery.com/jquery-latest.js"> 
</script>
<script src=
 "http://code.jquery.com/ui/1.12.1/jquery-ui.js">
</script>

 

The first thing we have to do is create an object that contains the code of my custom control. Within this object you have to define some standard functions that jQuery UI calls as it handles your custom control.

Notice that you are handing over how your control is managed but you get a lot back in return.

The one standard function you have to define is _create which is called by the framework when the user adds an instance of your control to a page.

This has no parameters but there are two supplied variables 

this.element 

which is the element you should build your custom control on and 

this.options

which is an object holding any options that the user set when creating the custom control.

Putting all this together we can now define our myWidget object:

myWidget = {
  _create: function() {
  var button = $("<button>");
  button.text("My Experimental Widget Button");
  $(this.element).append(button);
  }
};

Notice that this is exactly the same as our raw code but now the widget is appended to this.element. 

Now all we have to do is add the control to jQuery UI:

$.widget("iP.myButton", myWidget);

This adds myWidget to the iP namespace as myButton.  Following this the user can add an instance of your new button in exactly the same way they would add any other jQuery UI control i.e.:

$("div").myButton();

Notice that the name of the new control is myButton and it is added to the div element as before. 

Notice that the more usual style of using the widget factory function is to define the object with your widgets code on the fly e.g.

$.widget("iP.myButton", {
  _create: function() { 
  var button = $("<button>");
  button.text("My Experimental Widget Button");
  $(this.element).append(button); 
  }
}
);

 

Use which ever form you find presents a clearer presentation of your widget. 

Options

Creating a custom control just needs you to define a _create function but users generally want to customize the control by setting and modifying options. In jQuery UI this is done by passing an options object as part of the instantiation of the control.  

For example you could create a custom button using:

$("div").myButton({width:100,color:"red"});

Of course nothing is going to happen to the custom button unless we add some code to take account of the user specified options. All we need to do is add to the _create function something like: 

button.width(this.options.width)
button.css("background-color",
                 this.options.color)

This all works but now we have the problem of what happens if the user doesn't supply an option when creating the widget? 

The solution to this problem is that you can define an options object which stores the default values for all of the options you define. For example:

options:{width:null,color:"blue"},

Now if the user doesn't specify any of the options listed the defaults are used. What happens is that the system automatically merges the user specified options with the option object so keeping it up-to-date as an accurate reflection of the options in force. 

If you know jQuery UI you will also know that the user can retrieve current option values and set them at any time. For example:

$("div").myButton(); alert($("div").myButton("option","width")); $("div").myButton("option","width",200);
alert($("div").myButton("option","width"));

 

The get option call works because the framework simply retrieves the options object and returns the current value. However the set option call doesn't work. To make it work we have to implement the _setOption function which has to handle all of the options that could be set: 

_setOption: function(key, value) {
  switch (key) {
    case "width":
     ...
     break;
    case "color":
     ...
     break;
  }
}

For the width method we are simply going to need to change the width of the button but in general changing an option could mean a lot of work and so it is best to build functions to do the job. In this case it is simpler to just do it.

Our first problem is how to locate the button instance in the DOM. The problem is that the context this is set to the object that has our widgets code and not the DOM object we wish to change. The simplest thing to do is to add any properties you need in the _create function to keep track of the DOM components of your widget.

For example we need to keep track of the button object so we need to create a _button property in the _create function:

_create: function() {
 this._button = $("<button>");
 this._button.text("My Experimental
                            Widget Button");
 this._button.width(this.options.width)
 this._button.css("background-color",
                        this.options.color);                  
 $(this.element).append(this._button); }

Notice that it is a convention that all private variables and methods should start with an underscore. 

With this change we can now write:

_setOption: function(key, value) { 
  switch (key) { 
    case "width": 
     this._button.width(value); 
     break; 
    case "color":
     this._button.css("background-color",value);
     break; 
  } 
}

The only problem is that this doesn't change the internal state of the options object to reflect the new setting. You could make a direct assignment to the options object but it is much better to call the original _setOption supplied by the framework:

this._superApply(arguments);

This calls the _setOption in the prototype object and this by default updates the options object. 

Now if you try 

$("div").myButton(); alert($("div").myButton("option","width")); $("div").myButton("option","width",200);
alert($("div").myButton("option","width"));

You will see that the width option has changed on the button and on the options object. 

Notice that _setOption is called once for each option that the user sets. For example

 $("div").myButton("option",
               {width:100,color:"red"});

results in _setOption being called twice, once for width and once for color. If you want to handle option changes as a set then you need to define the _setOptions function which is called just one for each use of "option" and supplies the whole set of options being set. 

justjquery



Last Updated ( Sunday, 29 January 2017 )