Extending Widgets with the Widget Factory in jQuery UI
The Widget Factory in jQuery UI makes it easier to create widgets that extend the functionality of existing widgets. This way, you can build powerful widgets on top of existing ones or make subtle adjustments to the functionality of existing widgets.
Note: Before studying this section, it is necessary to understand what the Widget Factory is and how it works. If you are not familiar with this knowledge, please refer to the How to Use the Widget Factory section first.
Creating Widget Extensions
Creating a widget with the Widget Factory is done by passing the widget name and a prototype object to $.widget()
. The following example creates a "superDialog" widget in the "custom" namespace.
$.widget( "custom.superDialog", {} );
To support extensions, $.widget()
can optionally accept the constructor function of the parent widget. When specifying a parent widget, pass it as the second argument, after the widget name and before the widget prototype object.
As in the previous example, we will create a "superDialog" widget in the "custom" namespace. But this time, we pass the constructor function of the jQuery UI dialog widget ($.ui.dialog
), indicating that the superDialog widget should use the jQuery UI dialog widget as its parent.
$.widget( "custom.superDialog", $.ui.dialog, {} );
Here, the superDialog and dialog widgets are essentially equivalent, except for their names and namespaces. To make our new widget more distinctive, we can add some methods to its prototype object.
The prototype object of the widget is the last parameter passed to $.widget()
. So far, our examples have used an empty object. Now, let's add a method to this object:
$.widget( "custom.superDialog", $.ui.dialog, {
red: function() {
this.element.css( "color", "red" );
}
});
// Create a new <div>, convert it into a superDialog, and call the red() method.
$( "<div>I am red</div>" )
.superDialog()
.superDialog( "red" );
Now superDialog
has a red()
method that changes its text color to red. Note how the Widget Factory automatically sets this
to the instance object of the widget. For a list of all available methods and properties on the instance, visit the Widget Factory API documentation.
Extending Existing Methods
Sometimes, you need to adjust or add behavior to existing widget methods. You can specify the method name on the prototype object that needs to be overridden. The following example overrides the open() method of the dialog. Since the dialog is open by default, when this code runs, "open"
will be logged.
$.widget( "custom.superDialog", $.ui.dialog, {
open: function() {
console.log( "open" );
}
});
// Create a new <div>, and convert it into a superDialog.
$( "<div>" ).superDialog();
When this code runs, there is a problem. Since we have overridden the default behavior of open()
, the dialog no longer appears on the screen.
When we use a method on the prototype object, we are actually overriding the original method with a new one on the prototype chain.
To make the parent widget method available, the Widget Factory provides two methods - _super()
and _superApply()
.
Using _super()
and _superApply()
to Access Parent Widgets
$.widget( "custom.superDialog", $.ui.dialog, {
open: function() {
console.log( "open" );
this._super();
}
});
// Create a new <div>, and convert it into a superDialog.
$( "<div>" ).superDialog();
By calling this._super()
, we ensure that the original open()
method of the parent widget is also executed, allowing the dialog to display correctly while still logging the message.
This is a Chinese to English translation. Here is the English translation for the text:
_super() and _superApply() call the same methods in the parent widget. See the example below. Like the previous example, this one also overrides the open()
method to log "open"
. However, this time running _super()
calls the open()
of the dialog and opens the dialog.
$.widget( "custom.superDialog", $.ui.dialog, {
open: function() {
console.log( "open" );
// Invoke the parent widget's open().
return this._super();
}
});
$( "<div>" ).superDialog();
_super()
and _superApply()
are actually equivalent to the original Function.prototype.call()
and Function.prototype.apply()
methods. Therefore, _super()
takes an argument list, and _superApply()
takes an array as an argument. The example below demonstrates the difference between the two.
$.widget( "custom.superDialog", $.ui.dialog, {
_setOption: function( key, value ) {
// Both invoke dialog's setOption() method. _super() requires the arguments
// be passed as an argument list, _superApply() as a single array.
this._super( key, value );
this._superApply( arguments );
}
});
Redefining Widgets
jQuery UI 1.9 introduced the ability to redefine widgets. Instead of creating a new widget, we can simply pass the existing widget name and constructor to $.widget()
. The example below adds the same logging in open()
, but without creating a new widget.
$.widget( "ui.dialog", $.ui.dialog, {
open: function() {
console.log( "open" );
return this._super();
}
});
$( "<div>" ).dialog();
With this method, we can extend an existing widget method and still use _super()
to access the original method—all without creating a new widget, but by directly redefining the widget.
Widgets and Polymorphism
When interacting between widget extensions and their plugins, it's worth noting that the parent widget's plugin cannot be used to call methods on the child widget elements. The example below demonstrates this.
$.widget( "custom.superDialog", $.ui.dialog, {} );
var dialog = $( "<div>" ).superDialog();
// This works.
dialog.superDialog( "close" );
// This doesn't.
dialog.dialog( "close" );
In the example above, the parent widget's plugin, dialog()
, cannot call the close()
method on the superDialog element. For more information on calling widget methods, see Widget Method Invocation.
Customizing Individual Instances
So far, the examples we've seen have methods that extend the widget prototype. Methods overridden on the prototype affect all instances of the widget.
To demonstrate this, see the example below. Both instances of the dialog use the same open()
method.
$.widget( "ui.dialog", $.ui.dialog, {
open: function() {
console.log( "open" );
return this._super();
}
});
// Create two dialogs, both use the same open(), therefore "open" is logged twice. $( "<div>" ).dialog(); $( "<div>" ).dialog();
Sometimes, you only need to change the behavior of a specific instance of a widget. To achieve this, you need to use normal JavaScript property assignment, get a reference to the instance, and override the method. The following example demonstrates this.
var dialogInstance = $( "<div>" )
.dialog()
// Retrieve the dialog's instance and store it.
.data( "ui-dialog" );
// Override the close() method for this dialog
dialogInstance.close = function() {
console.log( "close" );
};
// Create a second dialog
$( "<div>" ).dialog();
// Select both dialogs and call close() on each of them.
// "close" will only be logged once.
$( ":data(ui-dialog)" ).dialog( "close" );
The technique of overriding methods for individual instances is perfect for one-time customizations.