Skip to content

Understanding jQuery UI widgets: A tutorial

Last modified May 31, 2012.

Updated for jQuery 1.8

This was written largely to help me make sense of using UI to create my own widgets, but I hope it may help others. "Widget" to me means a user-interface element, like a button or something more complicated like a popup date picker, but in jQuery UI terms it means a class, members of which are associated with HTML elements; things like Draggable and Sortable. In fact, not everything that I would have called a widget uses $.widget; the UI datepicker does not.

Dan Wellman has another tutorial that you may find helpful.

Modifying Elements: Plugins

That being as it may, let's use $.widget.

Let's take a paragraph of class target:


	<p class="target">This is a paragraph</p>
	

And lets make it green. We know how; $('.target').css({background: 'green'}).

Now, make it more general-purpose: a plugin:

$.fn.green = function() {return this.css({background: 'green'})};

But this allows us to perform some behavior on the selected elements; it does not leave us with any way to keep our plugin associated with that element, so we can do something with it later, like $('.target').off() to remove the green background, but only if we used green to put it there in the beginning. We also have no way of associating state with the element, to do $('.target').darker(), which would require knowing how green the element is now.

Keeping State in Plugins

We could create an object and associate it with an element using javascript expandos: element.myobject = new Myobject({'target': element}). Sample code would be:


$.fn.green2 = function() {
	return this.each(function(){
			if (!this.green) this.green = new Green($(this)); // associate our state-keeping object with the element
			this.green.setLevel(15);
	});
};
$.fn.off = function() {
	return this.each(function(){
		if (this.green) this.green.setLevel(16);
		delete this.green; // recover the memory
	});
};
$.fn.darker = function() {
	return this.each(function(){
		if (this.green) this.green.setLevel(this.green.getLevel()-1);
	});
};
$.fn.lighter = function() {
	return this.each(function(){
		if (this.green) this.green.setLevel(this.green.getLevel()+1);
	});
};

function Green(target){
	greenlevels = ['#000','#010','#020','#030','#040','#050','#060','#070','#080','#090','#0a0','#0b0','#0c0','#0d0','#0e0','#0f0','#fff'];
	this.target = target; // associate the element with the object
	this.level = 0;
	this.getLevel = function() { return this.level; }
	this.setLevel = function(x) {
		this.level = Math.floor(Math.min(greenlevels.length-1, Math.max(0,x)));
		this.target.css({background: greenlevels[this.level]});
	}
};

But this pollutes the $.fn namespace terribly, with off, darker and lighter. There are ways to create real namespaces within $.fn, but the usual design pattern is to use a string to specify which function to call. Thus, element.green2() to instantiate the plugin, element.green2('darker') or element.green2('lighter') to manipulate it:


$.fn.green2 = function(which){
	return this.each(function(){
		if (which === undefined){ // initial call
			if (!this.green) this.green = new Green($(this)); // associate our state-keeping object with the element
			this.green.setLevel(15);
		}else if (which == 'off'){
			if (this.green) this.green.setLevel(16);
			delete this.green
		}else if (which == 'darker'){
			if (this.green) this.green.setLevel(this.green.getLevel()-1);
		}else if (which == 'lighter'){
			if (this.green) this.green.setLevel(this.green.getLevel()+1);
		}
	});
};

function Green(target){
	greenlevels = ['#000','#010','#020','#030','#040','#050','#060','#070','#080','#090','#0a0','#0b0','#0c0','#0d0','#0e0','#0f0', '#fff'];
	this.target = target; // associate the element with the object
	this.level = 0;
	this.getLevel = function() { return this.level; }
	this.setLevel = function(x) {
		this.level = Math.floor(Math.min(greenlevels.length-1, Math.max(0,x)));
		this.target.css({background: greenlevels[this.level]});
	}
};

	<p class="target">This is a test paragraph</p>
	

The Problems with Associating an Object with a Plugin

But you get into trouble with circular references (note that "this.green = new Green($(this))" gives a DOM element a reference to a javascript object and "this.target = target" gives a javascript object a reference to a DOM element) and memory leaks: browsers (notably Internet Explorer) uses different garbage collectors for DOM elements and javascript objects. Circular references mean that each garbage collector thinks the other object is in use and won't delete them.

We also need to remember to reclaim the memory (with delete) if we no longer need the plugin.

jQuery solves the circular reference problem with the $.fn.data plugin: $(element).data('myobject', new Myobject({'target': element})). But now we've got a lot of "paperwork" to keep track of, and it hides the underlying program logic. As we know, design patterns reflect language weakness. If we are constantly re-implementing a pattern, we need to abstract it and make it automatic.

Solving the Problem: $.widget

That's where $.widget comes in. It creates a plugin and an associated javascript class and ties an instance of that class with each element so we can interact with the object and act on the element, without getting into trouble with memory leaks.

You still need to create the constructor of your class, but instead of a real constructor function, you need a prototype object with all the relevant methods. There are a few conventions: the function _create is called on construction, _init is called both on construction and for re-initializing the function and destroy is called on removal. All of these are predefined as empty functions but you can override them (and most likely will need to override _init). element is the associated jQuery object (what we called target above).

Widget methods that start with "_" are pseudo-private; they cannot be called with the $(element).plugin('string') notation


var Green3  = {
	_init: function() { this.setLevel(15); },
	greenlevels: ['#000','#010','#020','#030','#040','#050','#060','#070','#080','#090','#0a0','#0b0','#0c0','#0d0','#0e0','#0f0', '#fff'],
	level: 0,
	getLevel: function() { return this.level; },
	setLevel: function(x) {
		this.level = Math.floor(Math.min(this.greenlevels.length-1, Math.max(0,x)));
		this.element.css({background: this.greenlevels[this.level]});
	},
	darker: function() { this.setLevel(this.getLevel()-1); },
	lighter: function() { this.setLevel(this.getLevel()+1); },
	off: function() {
		this.element.css({background: 'none'});
		this.destroy(); // use the predefined function
	}
};

Notice it's all program logic, no DOM or memory-related bookkeeping. Now we need to create a name, which must be preceded by a namespace, like "ns.green" . Unfortunately the namespacing is fake; the plugin is just called $().green(). The constructor function is $.ns.green, but you never use that, so you might as well use the "official" namespace of "ui". But defining the widget couldn't be easier:


$.widget("ui.green3", Green3); // create the widget

Manipulating Widgets

What about our manipulating functions? All the functions defined in the prototype that don't start with an underscore are exposed automatically: $('.target').green3() creates the widgets; $('.target').green3('darker') manipulates them.

There are two kinds of plugin functions: getters and setters. Setters manipulate elements and can be chained in jQuery code; they just return the jQuery object they started with. For example, $('.demo').css({color: 'red'}).text('stuff'). Getters return information and break the chain, like text = $('.demo').text(). widget distinguishes between the two by the return value: if your function returns any value other than undefined, it assumes that it is a getter and returns that value (for the first element in the jQuery object, just like all jQuery getter functions). If it does not return a value, then it is assumed to be a setter and is called on each element in the jQuery object, and the jQuery object itself is returned for chaining.


	<p class="target">This is a test paragraph.</p>
	

Pass arguments to the manipulating functions after the name: $('.target').green3('setLevel', 5).

Data for Each Widget

The astute reader will have noticed that level is a class variable; the same variable is used for every green3 object. This is clearly not what we want; each instance should have its own copy. $.widget defines an object this.options for per-widget data. Thus:


var Green4  = {
	getLevel: function () { return this.options.level; },
	setLevel: function (x) {
		var greenlevels = this.options.greenlevels;
		var level = Math.floor(Math.min(greenlevels.length-1, Math.max(0,x)));
		this.options.level = level;
		this.element.css({background: greenlevels[level]});
	},
	_init: function() { this.setLevel(this.getLevel()); }, // grab the default value and use it
	darker: function() { this.setLevel(this.getLevel()-1); },
	lighter: function() { this.setLevel(this.getLevel()+1); },
	off: function() {
		this.element.css({background: 'none'});
		this.destroy(); // use the predefined function
	},
	options: { // initial values are stored in the widget's prototype
		level: 15,
		greenlevels: ['#000','#010','#020','#030','#040','#050','#060','#070','#080','#090','#0a0','#0b0','#0c0','#0d0','#0e0','#0f0', '#fff']
	}
};
$.widget("ui.green4", Green4);

And on creating an instance of a widget, pass an options object (the way most plugins do) and override the defaults: $('.target').green4({level: 8}).

Note that I also put the list of colors into the defaults object, so it too can be overridden. This widget probably shouldn't be called "green" anymore!


	<p class="target">This is a test paragraph.</p>
	

	<p class="target">
	This is a test paragraph called with .green4({
		level:3,
		greenlevels: ['#000','#00f','#088', '#0f0', '#880', '#f00', '#808', '#fff']
	}).
	</p>
	

Callbacks, or, Keeping the Lines of Communication Open

The programmer who is embedding our widget in his page may want to do other things when the widget changes state. There are two ways to alert the calling program that something has happened:

Tightly Coupled
The caller can provide a function to call at the critical point. jQuery jargon calls this a "callback;" it's used in animations and Ajax. We can create callback functions that the widget calls at critical points, and pass them to the widget-constructing plugin like any other option:

var Green5 = {
	setLevel: function(x){
		//...
		this.element.css({background: greenlevels[level]});
		var callback = this.options.change;
		if ($.isFunction(callback)) callback(level);
	},
	// ... rest of widget definition
};
$.widget("ui.green5", Green5);

$('.target').green5({change: function(x) { alert ("The color changed to "+x); } });
Loosely Coupled
Also called the Observer Design Pattern, the widget sends a signal to the programming framework and the calling program informs the framework that it wants to know about the signal. Events like clicks and keystrokes work like this, and jQuery allows the widget to create custom events and for the calling program to bind an event handler to that custom event:

var Green5 = {
	setLevel: function(x){
		//...
		this.element.css({background: greenlevels[level]});
		this.element.trigger ('green5change', level);
	},
	// ... rest of widget definition
};
$.widget("ui.green5", Green5);

$('.target').green5();
$('.target').bind("green5change", function(evt,x) { alert ("The color changed to "+x); });

$.widget allows both forms with the _trigger method. In a widget object, this._trigger(type, event, data) takes a type {String} with the name of the event you want (use some short verb, like 'change') and optionally a $.Event object (if you want to pass things like timestamps and mouse locations. Don't worry about event.type; _trigger changes it to the constructed event name), and any data to be passed to the handler. _trigger creates a custom event name of widgetName+type, like green6change (why it doesn't do type+'.'+widgetName the way jQuery expects is beyond me naming events this way has been the subject of some discussion), sets event.type = custom event name (creating a new $.Event if it was not provided) and calls this.element.trigger(event, data) and then looks for a callback with callback = this._getData(type) and calls it with callback.call(this.element[0], event, data).

Notice that this means the function signature is slightly different for the event handler and the callback if data is an array. element.trigger() uses apply to turn each item in the array into a separate argument. So this._trigger('change', 0, ['one', 'two']) requires an event handler of the form function(event, a, b) and a callback of the form function(event, data).

In practice, it's not as complicated as it sounds. For example, using both methods:


var Green5  = {
	getLevel: function () { return this.options.level; },
	setLevel: function (x) {
		var greenlevels = this.options.greenlevels;
		var level = Math.floor(Math.min(greenlevels.length-1, Math.max(0,x)));
		this.options.level = level;
		this.element.css({background: greenlevels[level]});
		this._trigger('change', 0, level);
	},
	_init: function() { this.setLevel(this.getLevel()); }, // grab the default value and use it
	darker: function() { this.setLevel(this.getLevel()-1); },
	lighter: function() { this.setLevel(this.getLevel()+1); },
	off: function() {
		this.element.css({background: 'none'});
		this._trigger('done');
		this.destroy(); // use the predefined function
	},
	options: {
		level: 15,
		greenlevels: ['#000','#010','#020','#030','#040','#050','#060','#070','#080','#090','#0a0','#0b0','#0c0','#0d0','#0e0','#0f0', '#fff']
	}
};
$.widget("ui.green5", Green5);

	<p class="target">This is a test paragraph with green level <span class="level">undefined</span>.</p> 
	

//  The on button above does the following:
$('.target').green5({
	change: function(event, level) { $('.level', this).text(level); } // callback to handle change event
});
$('.target').bind('green5done', function() { $('.level', this).text('undefined');alert('bye!') }); // event handler for done event

Involving the Mouse

Now, a lot of what we want to do with widgets involves mouse tracking, so ui.core.js provides a mixin object that includes lots of useful methods for the mouse. All we need to do is add the $.ui.mouse widget to our widget prototype:


var Green6 = {mouse-overriding function and widget-specific functions};
$.widget ('ui.green6', $.ui.mouse, Green6);

And override $.ui.mouse's functions (_mouseStart, _mouseDrag, _mouseStop) to do something useful, and call this._mouseInit in your this._init and this._mouseDestroy in your this.destroy. The mouse defaults are automagically including in your options object; see the mouse code for details.

Let's add some mouse control to our greenerizer:


Green6 = $.extend({}, $.ui.green5.prototype, { // leave the old Green5 alone; create a new object
	_init: function(){
		$.ui.green5.prototype._init.call(this); // call the original function
		this._mouseInit(); // start up the mouse handling
	},
	destroy: function(){
		this._mouseDestroy();
		$.ui.green5.prototype.destroy.call(this); // call the original function
	},
	// need to override the mouse functions
	_mouseStart: function(e){
		// keep track of where the mouse started
		this.xStart = e.pageX; // not in the options object; this is not something that can be initialized by the user
		this.levelStart = this.options.level;
	},
	_mouseDrag: function(e){
		this.setLevel (this.levelStart +(e.pageX-this.xStart)/this.options.distance);
	},
	options: {
		level: 15,
		greenlevels: ['#000','#010','#020','#030','#040','#050','#060','#070','#080','#090','#0a0','#0b0','#0c0','#0d0','#0e0','#0f0', '#fff'],
		distance: 10
	}
});
$.widget("ui.green6", $.ui.mouse, Green6);

	<p class="target">This is a test paragraph with green level <span class="level">undefined</span>.</p>
	

The ever-alert reader will note what we've just done: subclassed green5 to make green6, including calls to "super" methods. This ought to be abstracted out into its own method, something like $.ui.green5.subclass("green6", $.ui.mouse, {mouseStart: function(){}, mouseDrag: function(){}}) but that's a topic for another day.

{ 106 } Comments

  1. Anup | January 29, 2009 at 4:25 pm | Permalink

    Thanks for the post. Very interesting.

    Its good you pointed out the observer pattern, as I feel that is more flexible than the callback approach. It makes it a bit easier to unit test, sometimes, but also allows more than one thing to observe the events, if needed.

  2. Rob Desbois | February 10, 2009 at 5:36 am | Permalink

    Just in case anyone has the problem I did: if you get the error “$.Event is not a function” it’s because your jQuery and jQuery UI versions are incompatible.
    You need to use either:
    jQuery UI 1.5.3 with jQuery 1.2.6 (not 1.3+)
    jQuery UI 1.6rc5 with jQuery 1.3.1 (not 1.2.6)

    Obviously didn’t RTFM…

    Brilliant post, thank you very much!

    –rob

  3. Danny | February 10, 2009 at 10:16 am | Permalink

    @Rob Desbois:
    Yes. This tutorial uses jQuery UI 1.6 (right now it’s rc6, but the release version should not change), which requires jQuery 1.3. The old version of this page (http://bililite.nfshost.com/blog/2008/08/03/jquery-ui-widgets/) used jQuery 1.5, if you’re sticking with that.

    The fact that the UI team couldn’t get the new release out in time, so the “official” versions are incompatible was unfortunate. Hopefully, 1.6 will be released this week and we can all breath a sigh of relief.

    -Danny

  4. aleemb | February 12, 2009 at 8:03 pm | Permalink

    Thanks very much, it was a great read. I am new to jQuery so the preamble to widgets really helps recognize why jQuery is so friendly. I learnt about event namespacing just a few hours back out of need; hope they can switch the convention in widgets if possible.

    Looking forward to reading your post on extending widgets next.

    PS: Would be helpful if you dated your articles so it’s easier to gauge relevancy given the rapidly changing landscape.

  5. Danny | February 12, 2009 at 8:23 pm | Permalink

    @aleemb:
    Good idea on the dates; I added that. I also added a link to the discussion about event namespacing for widgets; the current way of doing it is reasonable.
    -Danny

  6. novachok | February 25, 2009 at 2:23 am | Permalink

    Very thanks, I fix my widget to work with UI 1.6
    Thank for this nice post

  7. Reggie Santos | March 5, 2009 at 4:16 am | Permalink

    Hi Danny,

    I tried all the examples in this tutorial and I seem to be having a problem with one. An error keeps occurring in this line (TypeError: this.trigger is not a function):

    this.trigger (‘green5change’, [level]);

    of the Observer Design Pattern application example.

    I tried changing this to:

    this.element.trigger(‘green5change’,[level]);

    and that seems to work.

    I’m quite new to jQuery and jQuery UI development so I would like to verify if I made the right change. Thanks!

  8. Danny | March 5, 2009 at 1:36 pm | Permalink

    @Reggie:
    You’re right; that code is an example but not part of the actual code so it never got tested. I’ve changed it now.
    Thanks!
    Danny

  9. Mark | March 7, 2009 at 7:09 pm | Permalink

    I’m trying to get the definitions of Green5 and Green6 into external Javascript.
    But the extend does not work as references to the $.ui.green5 do not exist yet.
    I have tried replacing with the variable.
    e.g.
    var Green6 = $.extend({}, Green5.prototype, $.ui.mouse,{
    but then I get an error about Green5.prototype not being defined.
    Of course I haven’t made it yet…
    Whats the approach for putting this in external Javascript file ?

  10. Danny | March 8, 2009 at 8:33 am | Permalink

    @mark:
    The var Green5 = {} statements define private variables; you can’t get to them; the only way is to use $.ui.green5.prototype.
    There’s no reason your external Javascript file couldn’t define $.ui.green5; just include the line $.widget("ui.green5", Green5); in the anonymous function that defines Green5.
    If Green5 and Green6 are in the same function, use var Green6 = $.extend({}, Green5, $.ui.mouse);

    -Danny

  11. Ran Davidovitz | April 27, 2009 at 11:51 pm | Permalink

    Thebasic thing that i dont see implemented in control is HTML markup that is embeded in it.
    I would like to develop a set of client side user control (in Jquery called widget) and reuse them, in those control i dont want to create the DOM elements using API but in amore UI friendly way (E.g. Markup).

    Where do you advise on doing it ?
    What is the best approach here ?

  12. Danny | April 28, 2009 at 2:45 pm | Permalink

    @ran:
    The widget pattern does not address generating HTML. The easiest way to create elements is with the $() function, $('<div><span>Hello, world</span></div>') but creating long strings gets ugly. There are plugins, like flyDOM that can help create complex elements, but what I have done mostly is use AJAX: create the markup in its own HTML file then use $(selector).load() to retrieve it. It makes debugging the HTML easier.
    Danny

  13. Danny | June 7, 2009 at 11:54 am | Permalink

    Joel wrote (but his comment was lost in the move to the new domain, sorry):

    When I was trying these code, I noticed that, each widget that extends $.ui.mouse requires an option “distance”, which is in $.ui.mouse.defaults. The mouse function won’t work without this option. While in the above case, ‘distance’ option has a special meaning to calculate the level and not demonstrate the problem i mentioned. Hope the author mention that and explains the usage of distance in mouse.defaults. :)

    Actually, this is exactly what mouse.defaults.distance is used for. It is the number of pixels that the mouse has to move before the _mouseDrag method is called; the step size of dragging, if you will. My code above means that the level will change for every step (calculated as the difference between the current x-value and the original x-value in pixels, divided by that step size).
    I hope that makes sense.
    –Danny

  14. Danny | June 7, 2009 at 8:28 pm | Permalink

    Joel emailed me personally to point out that in fact, $.ui.mouse._mouseDrag requires a distance option and that it is not automatically set. I have updated the tutorial to reflect this.

  15. chris | June 15, 2009 at 7:47 am | Permalink

    Thanks for the great tutorial!

    If I overwrite the Green4.destroy() function, what original or “superclass” destroy would I still need to call?

  16. Danny | June 15, 2009 at 10:03 am | Permalink

    @Chris:
    you would need to do

    $.ui.green4.prototype.destroy.call(this)

    to call the original destroy
    It’s ugly and inconvenient, which is why I use a widget subclassing framework.

  17. Matthew Sweet | June 24, 2009 at 10:44 pm | Permalink

    Awesome, thank you so much! I’ve been trying to find a good design pattern for plug-in development, but everything I found or came up with didn’t satisfy my requirements. Then I discovered widget, and then this page. Yay :) I think the pieces are starting to fall into place…

    Question: Do you think it’s safe to use $.widget? I don’t see any documentation about it on jQuery’s site, and am worried about future deprecation.

    Thanks again. This was a well written article!

  18. Matthew Sweet | June 24, 2009 at 10:48 pm | Permalink

    Ooops, my mistake – widget is from the ui core , and so the documentation is on the UI site. Rad. Thanks again…

  19. Danny | June 24, 2009 at 11:42 pm | Permalink

    @Matthew:
    Yes, $.widget is a good base for plugin design (that’s why the jQuery UI people made it, to refactor the basic stuff out of all the UI plugins) though it’s pretty heavy to include all of jQuery UI just to get that. You may be better off copying just the $.widget part (http://dev.jqueryui.com/browser/trunk/ui/ui.core.js lines 217-375 in the current build) into your own code.
    –Danny

  20. Marv | June 25, 2009 at 5:46 am | Permalink

    I’m testing Green5 and Green6 and having one problem:

    when I test the (‘#divGreen’).green6.(‘getLevel’) returns a jQuery object even though I included the line:

    $.ui.green5.getter = “getLevel”;

    in my widget definition code?

    Awesome tutorial BTW — helped tremendously, specially Green6 where we are sub-classing an existing widget!

    –Marv

  21. Danny | June 25, 2009 at 3:00 pm | Permalink

    @Marv:
    The widgets are completely separate; if you are using green6 then you need to set $.ui.green6.getter = "getLevel";. Note that the getter is going to be obsolete (a good thing!) with the next iteration of jQuery UI.
    –Danny

  22. Liu Peng | July 16, 2009 at 4:46 am | Permalink

    This tutorial is what I need!
    I also create some widgets reference other jQuery UI widget like datepicker, tabs, but your tutorial let me understand the jQuery UI more clearly, thanks again for your proffer.

    P.S: I can not believe that you are a pediatric hospitalist!

  23. Danny | July 17, 2009 at 12:37 pm | Permalink

    @Liu:
    Thanks for the feedback. As for the PS, is it that you can’t believe I’m so good as to be able to code while working 24 hour hospital shifts or that you can’t believe I’m so foolish as to spend my life doing the scut that that normal doctors can’t wait to get out of after residency?

  24. Liu Peng | July 20, 2009 at 12:45 am | Permalink

    hehe, I really express my admiration for your useful tutorial while working 24 hour hospital shifts. You are a doctor and I’m a software developer, you can write a good tutorial and I can’t.
    Your timepickr is a good stuff, I want used it in my project.

  25. Scott Morrison | September 1, 2009 at 10:37 am | Permalink

    Thanks for the awesome tutorial :)

  26. Yash | September 25, 2009 at 4:23 am | Permalink

    This tutorial is what I need!

  27. thetoolman | November 30, 2009 at 4:18 am | Permalink

    Here is a template to start your own widget:

    (function($) {

    $.widget(“ui.widgetName”, {

    // options: provided by framework
    // element: provided by framework

    _init: function() {

    },

    log: function(msg) {
    if(!this.options.log) return;

    if(window.console && console.log) { // firebug logger
    console.log(msg);
    }
    },

    destroy: function() {
    $.widget.prototype.apply(this, arguments); // default destroy
    }

    });

    $.extend($.ui.widgetName, {
    version: “@VERSION”,
    getter: “length “, //for methods that are getters, not chainables

    defaults: {
    optionA: “bar”,
    suffixA: “foo”
    }
    });

    })(jQuery);
    /* END */

  28. Danny | December 2, 2009 at 8:19 pm | Permalink

    @thetoolman:
    That’s a good skeleton widget. I develop a more extensive one in my post about extending widgets
    –Danny

  29. danig | December 7, 2009 at 4:15 pm | Permalink

    excellent post! i always found hard to understand jqueryUI… until now… thanks!

  30. Bryan Berry | January 5, 2010 at 4:57 am | Permalink

    Awesome tutorial, tks a lot.

  31. Lindsay | January 7, 2010 at 1:36 pm | Permalink

    Hi Danny. First I wanted to say a huge thank you for this tutorial. It is one of the only ones I have found that goes into detail about how to use the jQuery widget framework and has been extremely helpful to me.

    You mention the ui.core “mixin” for the mouse. Do you know if there is also one for keypress events? And, is there any documentation out there that details those extensions and any others that are available via ui.core? I have had a heck of a time finding documentation on the jQuery ui that doesn’t involve the widgets they have prebuilt. Any help is much appreciated!

    Thanks again for the tutorial!

  32. Danny | January 8, 2010 at 10:56 am | Permalink

    @Lindsay:
    The only other documentation on building widgets is the official one, which isn’t very helpful. That’s why I had to write this, as a tutorial for myself! There is no keypress mixin code; you would have to write your own with something like
    this.element.bind("keypress."+this.widgetName, function(){...}). The widgetName is there to
    allow you to unbind your handlers without affecting others. $.ui.keyCode exposes some useful names for key codes; use the Source to see them.

    I have some keypress-simulating code in my sendkeys plugin that may be helpful.

    Hope this helps!

    –Danny

  33. Kragen Javier Sitaker | February 11, 2010 at 9:31 am | Permalink

    Thanks for the excellent tutorial! I just wrote my first jQuery UI widget using it (nothing useful to anyone else, at the moment…)

    At some point I got confused by the text, “$.widget defines two more functions that let us store and retrieve data for each instance individually: _setData and _getData,” and ended up using _setData and _getData to set and get some widget-private data: this._setData(‘foo’, something). Then I realized that I could just use this._foo = something, and my life got easier.

    It may not be worthwhile incorporating this into the tutorial if you incorporate a clarification for everything anyone ever gets confused by, you will end up with something like the unusably-verbose Perl documentation but I submit it for your consideration.

  34. Pipeman | February 13, 2010 at 5:33 am | Permalink

    Amazing, I can’t believe they don’t mention this widget structure in the ‘authoring a plugin’ docs – this is essential for plugins with states – many thanks & a link to this should definitely go here: http://docs.jquery.com/Plugins/Authoring

  35. Danny | February 13, 2010 at 9:22 pm | Permalink

    @Kragen Javier Sitaker:
    Yes, this._foo works for private data. this._getData('foo') actually uses this.options.foo so it can be used for both private data and public options (with $(selector).widget('option', 'foo')). I tried to keep the tutorial as limited as possible; for all the details I would look at the source code (jquery-ui.googlecode.com/svn/tags/1.7.2/ui/ui.core.js for the most
    recent release version); it’s not that complicated.
    Be aware, though, that many things are changing for version 1.8. I’ll try to update the tutorial when it is officially released (meaning when it’s on the Google Ajax API server).
    –Danny

  36. Danny | February 13, 2010 at 9:27 pm | Permalink

    @Pipeman:
    Technically, jQuery UI is a project independent from jQuery itself, so you can write plugins without UI, but I agree. Saving state is so often useful that I almost always start my
    plugins from a UI widget. The problem is that if you aren’t using the whole UI, you are including a huge amount of unnecessary stuff, so you may want to include just the widget code (which has been refactored out in release candidate 1.8 in jquery.ui.widget.js.
    –Danny

  37. rob | February 21, 2010 at 2:41 pm | Permalink

    great, thanks..that finally somebody understands the f*kn pain of the plugindevelopment!

    br
    rob

  38. Charles Phillips | March 22, 2010 at 4:06 pm | Permalink

    This is a great article. I just wanted to add, anyone looking to change or set the widget defaults, the API has changed! I looked around for a good half-hour before I found it :/ .

    It went from
    $.ui.dialog.defaults
    to
    $.ui.dialog.prototype.options

    http://forum.jquery.com/topic/how-to-set-dialog-defaults-in-1-8

    Cheers!

  39. Raymond | March 24, 2010 at 10:15 am | Permalink

    @Danny Thanks for this great resource! Now that 1.8 is out do you plan on updating this tutorial?

  40. Danny | March 24, 2010 at 8:55 pm | Permalink

    @Raymond and Charles Phillips:
    Is 1.8 officially out? I’ll have to update this, and all of my plugins. It will have to wait until after Passover, but I will get around to it!
    –Danny

  41. Raymond | March 25, 2010 at 3:45 pm | Permalink

    @Danny Yeah, 1.8 was released earlier this week. Thanks for the quick reply. Looking forward to your tutorial update!

    http://blog.jqueryui.com/2010/03/jquery-ui-18/
    http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.js

  42. Danny | March 25, 2010 at 4:57 pm | Permalink

    @Everyone:
    Page is now updated for jQuery 1.8. Whoo hoo!
    –Danny

  43. Steven Black | March 27, 2010 at 11:07 pm | Permalink

    Wow. This is an awesome article.

    The clarification that the options property is to be used for widget-specific data is excellent, really helpful.

  44. Manoj Sharma | April 6, 2010 at 12:32 am | Permalink

    Hi, Can you help me- Quest. is — I want to drag and drop object in jquery and also store in xml file. We have already drag and drop used but saving in xml . Please give me idea.
    Thnaks

    manoj_koderma@yahoo.com

  45. Danny | April 7, 2010 at 10:38 am | Permalink

    @Manoj Sharma:
    I don’t know much about XML. If you have a jQuery-related question, ask it on the jQuery forum. Sounds as though you have the drag and drop working; if not, look at jQuery droppable.
    –Danny

  46. Drew Wells | April 7, 2010 at 11:54 am | Permalink

    I have come upon a problem with my jQuery UI widget. I have a default set of options like, options.repeat = 0.

    I initialize an instance setting repeat to 30. Later I reuse the same div and reinitialize my plugin again without specifying repeat. The value of options.repeat is still 30 instead of the default 0. I considered destroying the entire instance and rebuilding everything to recreate this automatic structure, but that seems like overkill.

  47. Danny | April 8, 2010 at 4:30 pm | Permalink

    @Drew Wells:
    Your reinitializing code (should be in _init) could set this.options = $.extend({}, $.ui.mywidget.prototype.options, options) to make sure all the options are reset. Just copy the defaults (stored in $.ui.mywidget.prototype.options) rather than extending the old options.
    –Danny

  48. pThomas | April 27, 2010 at 10:41 am | Permalink

    Thanks for the tutorial, i just downloaded and started playin with the jQuery UI and your tut. surely helped to get me started. Also thanks to Charles Phillips for the updated UI Docs on default themes.

  49. Fox | May 13, 2010 at 12:50 am | Permalink

    Thanks!

  50. Naveen | May 27, 2010 at 9:37 am | Permalink

    Hi friends,
    i just need to know that is their any suit function for 10-12-1200+100+1456 =>answer

    i need a plugin or function to answer for the above one.is ther any jquery functions is ther ??

    Thanks in advance

  51. Danny | May 27, 2010 at 7:03 pm | Permalink

    @Naveen:
    You’re better off asking jQuery questions on the jQuery forum

  52. czerasz | July 14, 2010 at 3:22 pm | Permalink

    Great article! Thanks for sharing Your experience. Greetings from Poland.

  53. Les | July 26, 2010 at 10:11 am | Permalink

    Thanks Danny. This is a great quick intro to widgets.

  54. Daniel Cricco | September 3, 2010 at 8:41 am | Permalink

    Great article!! It helps me a lot to start developing my own widgets

  55. HackingHub | September 26, 2010 at 11:04 am | Permalink

    Some interesting stuff there! I love the fact that you can press ‘test’ and it shows you a small demo of the script (:

    I’ll be using that ‘darker’ feature on one of my sites. almost exactly what I was looking for :D

    Thanks!

  56. Thaha | October 12, 2010 at 4:02 am | Permalink

    Wonderful article. Great that you covered all aspects of the work. Thank you very much.

  57. senthil.srinivasan | October 14, 2010 at 1:10 am | Permalink

    Good one , nice post for Widgets, keep doing .

  58. Jaime | October 14, 2010 at 1:49 pm | Permalink

    Thanks you, I just finished my first plugin :D

  59. Using jQuery | October 26, 2010 at 11:45 pm | Permalink

    I have only used jQuery’s UI once before. Great tutorial

  60. Praveen | November 19, 2010 at 2:02 am | Permalink

    I was trying to create a widget for very first time after reading this tutorial. it is working when i call
    $(‘.demo’).green(); // returns output
    but when i try to call
    $(‘.demo’).green(‘darker’); nothing works out kindly suggest what is error
    Since error console does not reflect any error checked on firefox,IE,chrome and opera. Plz rely

    (function($){
    var Green3 = { _init: function() { this.setLevel(15); },
    greenlevels: ['#000','#010','#020','#030','#040','#050','#060','#070','#080','#090','#0a0','#0b0','#0c0','#0d0','#0e0','#0f0', '#fff'],
    level: 0,
    getLevel: function() { return this.level; },
    setLevel: function(x) { this.level = Math.floor(Math.min(this.greenlevels.length-1, Math.max(0,x))); this.element.css({background: this.greenlevels[this.level]}); },
    darker: function() { this.setLevel(this.getLevel()-1); },
    lighter: function() { this.setLevel(this.getLevel()+1); },
    off: function() { this.element.css({background: ‘none’}); this.destroy(); }
    };
    $.widget(“ui.green3″, Green3);
    })(jQuery);

  61. Frank | November 20, 2010 at 5:54 pm | Permalink

    Danny, you made my day with your post. Very thorough step-by-step description on what to do and what’s going on. If just more tutorials were like that.

    Thx a lot

    Greetings from 3N 11E, Central Africa
    Frank

  62. Webbdesign | November 21, 2010 at 9:17 am | Permalink

    Now I have followed your examples, all of them :) Great tutorial and gave me some good insights

  63. Danny | November 21, 2010 at 3:40 pm | Permalink

    @Praveen:
    I don’t see anything wrong with what you wrote. Remember you have to initialize the widget first (with $('demo').green3()) before you manipulate it (with $('demo').green3('darker').
    –Danny

  64. Younos Aboulnaga | December 14, 2010 at 7:18 am | Permalink

    Great article.. really helpful. I want to add a little warning about how _trigger() handles names of the custom events : AFTER ADDING THE PREFIX THAT EVERYBODY TALKS ABOUT, IT CALLS toLowerCase().. i don’t know the logic behind this, but what matters it, when you bind a function to this custom event, make sure to write all the name in lowercase, no matter what the real names are.

  65. Danny | December 14, 2010 at 8:39 am | Permalink

    @Younos Aboulnaga:
    Thanks for the heads up. I’m surprised they bother doing this, though most names are lowercase.
    –Danny

  66. bruce | December 18, 2010 at 4:02 pm | Permalink

    Great tutorial! Like some other commenters, I’m also new to jQuery, but I was able to follow along and it helped a lot.

    I do have two small questions about this code in the Loosely Coupled example.

    “setLevel = function(x){
    //…
    this.element.css({background: greenlevels[level]});
    this.element.trigger (‘green5change’, [level]);”

    Is there significance to putting an equals sign (instead of a colon) in between the text setLevel and function?

    Also, in the last arg passed to trigger, do the square brackets around level do anything special? (I’d have guessed level would be passed in without brackets.)

    Thanks!

  67. Danny | December 18, 2010 at 9:03 pm | Permalink

    @Bruce:
    1. You’re right; the equals is a typo. I’ve corrected it. Thanks! That’s what I get for writing an example that isn’t actually implemented.
    2. jQuery trigger takes an array for its second parameter, which is what [level] is: an array with one element. However, as I look at the source code rather than the documentation, it is clear that trigger will do that automatically, so you are right on that as well. Nice catch!
    –Danny

  68. swap games | February 2, 2011 at 12:46 pm | Permalink

    Very, very, very sick tutorial!
    Currently experimenting with jQuery UI

  69. Danny | February 2, 2011 at 7:27 pm | Permalink

    @swap games
    I assume “sick” is a typo for slick or some some South African slang I’m not familiar with, because your comment sounds positive.
    –Danny

  70. amixyue | February 15, 2011 at 12:13 am | Permalink

    good essay and inspires me quite a lot . Thanks to bililite .

  71. Samuel ROSSILLE | February 17, 2011 at 11:26 am | Permalink

    Hello and thanks for this good tutorial.
    There is still something I don’t understand about a widget’s instance-specific data. In the “Data for Each Widget” section, I understand that “this._myProperty” is shared between all instances of a given widget class.
    This seems weird to me for the following reasons:
    1/ Most of JQuery UI builtin widgets store a lot of instance-specific private data in “this” from their widget’s methods (see “jquery.ui.slider.js” for an example).
    2/ I have written a simple test to check this, and it seems that there is no mix between 2 instance of same widget when i store properties using this._myProperty
    3/ How could this.options refer to different objects for different instances, and this._yourPrivateProperty always refer to the same object for different instances… ?
    Is there a mistake in your the “Data for Each Widget” section, or have i missed something ?

  72. Danny | February 17, 2011 at 12:22 pm | Permalink

    @Samuel Rossille:
    You are right, things are more complicated and subtle than I presented it here. If you have:

    function Class() {}
    Class.prototype.property = 'prototype property';
    var obj = new Class();
    

    Then if you just read obj.property, you’re getting the prototype’s member, which is shared among all the objects. If you write to the property, like obj.property='object property', then you create a new property of obj alone. In general, you never notice this and have no problems with shared data, since if you haven’t written to the property you want to use the shared one.
    However, if the property if an object, then you can get into trouble, since obj.prop.foo = 'new value' reads obj.prop and assigns a value to the foo property of the shared obj.prop.
    In the jQuery UI code, this.options is created anew for each instance (with this.options = $.extend({}, this.options, options). Note how the options property is overridden after being read from the shared version.).
    I hope this makes some sense (and I’ve gotten the details right! Thinking about this stuff makes my brain hurt; that’s why I write this blog: so I only have to think the hard thoughts once!).
    –Danny

  73. Paul Schlereth | April 8, 2011 at 9:28 am | Permalink

    Getting errors when converting over from the entire jquery-ui-1.8.9.custom.min.js to just jquery.ui.widget.js. The error in Firebug reads ‘base is not a constructor’ and is telling me jquery.ui.widget.js is at fault. I’ve added jquery.ui.core.js prior to this in a failed attempt to fix. Help? Thanks again for the great tutorial!

  74. Danny | April 8, 2011 at 11:48 am | Permalink

    @Paul Schlereth:
    The last example of the tutorial uses jquery.ui.mouse.js for the mouse handling routines. Is your error being called when you click those buttons?
    –Danny

  75. Paul Schlereth | April 8, 2011 at 12:05 pm | Permalink

    @Danny

    Actually it happens on load – but that did fix it! Thanks! Lastly, is there any reason why this doesn’t seem to return anything?:
    $(‘.distance’, this).text(distance);

  76. Danny | April 8, 2011 at 12:56 pm | Permalink

    @Paul Shlereth:
    I’d have to analyze the program flow, but I guess it’s creating the widget on load. Makes sense; glad it works.
    $(…).text(distance) should set the text to the value of distance and return the jQuery object created with $(…). Does $(‘distance’, this) return anything?
    –Danny

  77. Florian | April 11, 2011 at 3:35 am | Permalink

    What’s best practice to overwrite standard jquery ui functions / methodes?
    Right now we did it directly in the jquer.ui.js but this prevents us from
    updating the library when bugfixes arrive. Any idea?

    thx
    Florian

  78. Danny | April 11, 2011 at 8:50 am | Permalink

    @Florian:
    That’s a question best addressed to the jQuery forum. That said, the usual way is with “Monkey Patching” to override the original routine. Something like:

    // I want to override jQuery.foo
    (function($){ // make my variables private
      var oldfoo = $.foo; // save the old version
      $.foo = function(){ // create the new version
        doSomethingCool();
        oldfoo(); // can still use the original version
      };
    })(jQuery);
  79. chandru.v | April 21, 2011 at 4:13 am | Permalink

    i’m new to widget designing and jquery ,i’m using FireFox 4.0 when clicks the button it wont fires please check it

    thank s in advance

  80. Danny | April 21, 2011 at 7:26 am | Permalink

    @chandru.v:
    I’m not at my computer (which is running Firefox 4) but I’ll check on it when I can. Can you run Firebug and see if you’re getting any JavaScript errors?
    –Danny

  81. Thomas | July 25, 2011 at 9:00 pm | Permalink

    Thanks for the excellent tutorial.

    Ive ported the example to CoffeeScript – In my opinion its looking quite a bit cleaner and nicer…

    http://stackoverflow.com/questions/6824428/how-to-improve-jquery-widgets-written-in-coffeescript

  82. Danny | July 26, 2011 at 9:13 am | Permalink

    @Thomas:
    Looks interesting, though I’ve never been inspired to try CoffeeScript (no flame wars, please; I acknowledge it’s a matter of taste and experience). I can think about closures directly and I like semicolons; I suppose if I had more Python and Ruby experience I would be gushing with enthusiasm for CoffeeScript.
    Danny

  83. Descargar | August 23, 2011 at 12:43 pm | Permalink

    Excellent stuff ! i will suscribed to your RSS dude ;)

  84. Glen_Whitley | September 18, 2011 at 8:12 pm | Permalink

    Thank, really usefull

  85. baidu ways | October 22, 2011 at 9:11 am | Permalink

    hey, this power be bit offtopic, but i am hosting my place on hostgator and they wishes interrupt my hosting in 4days, so i would like to ask you which hosting do you purpose or recommend?

  86. Danny | October 22, 2011 at 8:09 pm | Permalink

    @baidu:
    I use 1and1.com, on their cheapest plan. It’s old-line LAMP (linux-apache-php-mysql) only, and not updated very frequently, and down for an hour once or twice a week. In short, it’s worth everything I pay for it and I have no complaints, but it’s nothing fancy.
    –Danny

  87. Mark William | November 3, 2011 at 5:20 am | Permalink

    Very very important tutorial.
    Thank you somuch for this wonderful post.

  88. Cordyceps | November 8, 2011 at 3:08 pm | Permalink

    That is very nice what I did not undestand is how do you call setLevels?
    Can setlevels be called externally?

  89. Danny | November 8, 2011 at 3:41 pm | Permalink

    @Cordyceps:
    This may have been unclear, but the way to call all methods of UI widgets is by calling the plugin with the name of the method:
    $('.target').green6('setLevel', 7);
    The UI documentation goes into using widgets in more detail.
    –Danny

  90. Chemises homme | December 8, 2011 at 4:25 am | Permalink

    Thank dany but,
    why you don’t use the toggle function ?

  91. Danny | December 8, 2011 at 6:38 pm | Permalink

    @Chemises homme (I assume you are a real person who designed that site):
    Yes, for turning the color off and on in real life you would use a simple toggle. That was simply an example of how to create a widget; the example needed to be simple enough to not “get in the way” of the explanation. Actual widgets would be far more complex and need to store state and respond to events.
    –Danny

  92. Rafi | December 8, 2011 at 9:41 pm | Permalink

    Professionally developed and ready to use jQuery widgets.

  93. Danny | December 9, 2011 at 12:29 pm | Permalink

    @Rafi:
    Your comment is just an advertisement for your own set of widgets, which as far as I can tell to not even use jQuery UI. But they look cool and may give people ideas for their own widgets, so I’ll let it slide.

  94. brice | May 7, 2012 at 3:47 am | Permalink

    there is an error in function green2 ( first version )

    should be this :
    if (!this.green) this.green = new Green($(this)); // associate our state-keeping object with the element
    instead of this :
    if (!this.green) this.green = new Green(this); // associate our state-keeping object with the element

    thanks for the tuto ! very interesting !

  95. Danny | May 7, 2012 at 8:38 am | Permalink

    @brice:
    You are correct. I have corrected it.
    –Danny

  96. Chí Thanh | July 16, 2012 at 9:59 pm | Permalink

    Thanks! This post is very helpful for me.

  97. nyc | July 29, 2012 at 12:35 am | Permalink

    Still assimilating this well-crafted presentation.

    Can you tell us why widget destroy is not underscored?

  98. Danny | July 31, 2012 at 8:00 am | Permalink

    @nyc:
    The underscored methods like _init, are private; they cannot be called with $(...).widget('methodname'). You may want to remove a widget with all its data, so you can call $(...).widget('destroy').
    You never call _init directly; you create a widget with $(...).widget(options).
    Hope this helps.
    –Danny

  99. nyc | August 2, 2012 at 10:04 pm | Permalink

    Got it, thanks – in fact I saw an example of client code doing just that since I posted.

    Btw, “sick” is home-growed American slang as far as I can tell :)

  100. Trev | August 19, 2012 at 12:41 am | Permalink

    Thanks for writing this up and taking the time to include working examples.

  101. moises gil | August 19, 2012 at 6:11 am | Permalink

    Dear Sirs

    I would like to know how can I insert and work with the JQuery UI software.
    Your help will be appreciate
    rgds

  102. Danny | August 19, 2012 at 8:27 am | Permalink

    @Moises Gil:
    I can’t offer introductory tutorials, but there are lots of them out there: http://www.bing.com/search?q=jquery+ui+tutorial.
    –Danny

  103. jqueryheaven | November 22, 2012 at 2:23 pm | Permalink

    Actually, this is a awesome starter tutorial, and introduce some basics of jquery

  104. James Sandberg | November 24, 2012 at 12:53 pm | Permalink

    Great article. thanks for putting in such efforts for us all.

  105. Derek | December 13, 2012 at 10:45 am | Permalink

    Do tools like JSHint freak out when you call methods that don’t immediately exist?

    Such as this example in Green5:

    `this._trigger(‘done’);`

  106. Danny | December 13, 2012 at 11:31 am | Permalink

    @Derek:
    I don’t know; I’ve never done it. It should be OK, since the _trigger method is defined on the widget, so if JSHint is smart enough to know what this is, it should know what methods are defined.
    –Danny

{ 31 } Trackbacks

  1. [...] programming from a very occasional volunteer webmaster Skip to content HomeAboutAcknowledgementsUnderstanding jQuery UI widgets: A tutorialExtending jQuery UI Widgets { 2009 04 23 [...]

  2. [...] Understanding jQuery UI widgets – A tutorial [...]

  3. ??????? jQuery UI: ??????? ??????? | February 23, 2010 at 5:15 am | Permalink

    [...] ????????? ??????? ?????? Understanding jQuery UI widgets: A tutorial. [...]

  4. [...] http://bililite.nfshost.com/blog/understanding-jquery-ui-widgets-a-tutorial/ [...]

  5. [...] is easy to use within other design patterns. I have used this when creating widgets with $.widget (jQuery UI Widget factory), currently my favorite way for developing jQuery [...]

  6. [...] blog posts about widget [...]

  7. [...] Understanding jQuery UI widgets A tutorial [...]

  8. [...] This tutorial was a great start for me:http://bililite.nfshost.com/blog/understanding-jquery-ui-widgets-a-tutorial/ [...]

  9. [...] .draggable()! Daniel Wachsstock wrote a gorgeous blog post about using the Widget Factory called “Understanding jQuery UI widgets: A Tutorial”. It was game-changing for [...]

  10. [...] http://bililite.nfshost.com/blog/understanding-jquery-ui-widgets-a-tutorial/ [...]

  11. Dijit?jQuery UI Widget?? – Fan's blog | November 3, 2010 at 1:36 am | Permalink

    [...] ??????????????????????????????$(‘.target’).green3()??target element????widget????????DOM???????????JavaScript?????$(‘.target’).green3(‘darker’)???JS????darker??? [...]

  12. [...] Creating Custom jQuery Widget Posted: November 21, 2010 by Rupesh in jQuery Tags: JQuery 0 Hi I just came across a greate website http://bililite.nfshost.com/blog/understanding-jquery-ui-widgets-a-tutorial/ [...]

  13. [...] 4- How to Use the jQuery UI Autocomplete Widget [...]

  14. [...] http://bililite.nfshost.com/blog/understanding-jquery-ui-widgets-a-tutorial/ [...]

  15. [...] Hacking at 0300 : Understanding jQuery UI widgets: A tutorial [...]

  16. [...] as possible. Since this was my first trip around the jQuery ui block, I found bililite.com’s jQuery UI Widget tutorialan invaluable [...]

  17. [...] Understanding jQuery UI Widgets: A Tutorial, Hacking at 0300 [...]

  18. [...] Understanding jQuery UI Widgets: A Tutorial, Hacking at 0300 [...]

  19. [...] Understanding jQuery UI Widgets: A Tutorial, Hacking at 0300 [...]

  20. [...] Understanding jQuery UI Widgets: A Tutorial, Hacking at 0300 [...]

  21. [...] Understanding jQuery UI Widgets: A Tutorial, Hacking at 0300 [...]

  22. [...] Understanding jQuery UI Widgets: A Tutorial, Hacking at 0300 [...]

  23. [...] Understanding jQuery UI Widgets: A Tutorial, Hacking at 0300 [...]

  24. [...] Before jump right into the code, there are some basic things that you need to know, you can learn the jQuery Plugin authoring basics, practices, and common pitfalls in developing the plugin here [...]

  25. Write your own jQuery widget | July 4, 2012 at 5:08 am | Permalink

    [...] http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/ [...]

  26. F1.net » Blog Archive » jQuery Tutorials | September 14, 2012 at 12:58 am | Permalink

    [...] http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/ [...]

  27. How to Make JQueryUI Widgets | October 9, 2012 at 11:18 am | Permalink

    [...] Understanding jQuery UI widgets: A tutorial [...]

  28. [...] “Understanding jQuery UI Widgets: A Tutorial,” Hacking at 0300 [...]

  29. [...] trying to write a jQuery widget following the model given here. Here is a snapshot of the [...]

  30. […] “Understanding jQuery UI Widgets: A Tutorial,” Hacking at 0300 […]

  31. […] 了解jQuery的UI部件 – 教程 […]

Post a Comment

Your email is never published nor shared. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.