jQuery UI Extending Widgets with the Widget Factory
jQuery UI's Widget Factory 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 chapter, you need to understand what the Widget Factory is and how it works. If you are not familiar with this knowledge, please first read theHow to Use the Widget Factorychapter.
Creating Widget Extensions
Creating widgets with the Widget Factory is done by passing$.widget()a widget name and a prototype object. The example below creates a "superDialog" widget in the "custom" namespace.
$.widget( "custom.superDialog", {} );
To support extension,$.widget()optionally accepts the constructor of the widget to be used as the parent widget. When specifying a parent widget, pass it as the second parameter, after the widget name and before the widget prototype object.
Like the example above, the following also creates a "superDialog" widget in the "custom" namespace. But this time it passesjQuery UI's dialog widgetconstructor ($.ui.dialog), indicating that the superDialog widget should use the jQuery UI dialog widget as its parent widget.
$.widget( "custom.superDialog", $.ui.dialog, {} );
Here, the superDialog and dialog widgets are essentially equivalent, just with different names and namespaces. To make our new widget more distinctive, we can add some methods to its prototype object.
The widget's prototype object is the last parameter passed to$.widget(). So far, our example has 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" );
NowsuperDialoghas ared()method, which changes its text color to red. Note how the Widget Factory automatically setsthisto the instance object of the widget. To see a list of all available methods and properties on the instance, please visitthe 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 as the name of the method to override on the prototype object. The following example overrides dialog'sopen()method. 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 running this code, there is a problem. Because we overrodeopen()default behavior, the dialog no longer displays on the screen.
When we use a method on the prototype object, we are actually overriding the original method, using a new method in the prototype chain.
To make parent widget methods available, the Widget Factory provides two methods -_super()and_superApply()。
Using_super()and_superApply()to access the parent widget
_super()and_superApply()and call the same method on the parent widget. See the example below. Like the previous example, this example also overridesopen()method to log"open". However, this time running_super()calls the dialog'sopen(), 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()It is actually equivalent to the originalFunction.prototype.call()andFunction.prototype.apply()method. Therefore,_super()accepts an argument list,_superApply()accepts an array as its argument. The following example 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 added the ability to redefine widgets. Therefore, instead of creating a new widget, we only need to pass$.widget()an existing widget name and constructor. The following example addsopen()the same logging to ..., but not by creating a new widget.
$.widget( "ui.dialog", $.ui.dialog, {
open: function() {
console.log( "open" );
return this._super();
}
});
$( "<div>" ).dialog();
Through this method, we can extend an existing widget method, but we can still use_super()to access the original method - this is done not by creating a new widget, but by directly redefining the widget.
Widgets and Polymorphism
When interacting between widget extensions and their plugins, it is worth noting that the parent widget's plugin cannot be used to call methods on child widget elements. The following example 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 theclose()method on the superDialog element. To learn more about calling widget methods, please refer toWidget Method Calls。
Customizing Individual Instances
So far, the examples we've seen all have methods extended on the widget's prototype. Overriding methods on the prototype affects all instances of the widget.
To demonstrate this, see the example below. Both instances of the dialog use the sameopen()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 single instance of a widget. To do this, you need to use normal JavaScript property assignment, get a reference to the instance, and override the method. This is shown in the example below.
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 on an individual instance is perfect for one-off customizations.
Other Extensions