发布于: jQuery UI > Widget Factory

使用小部件工厂扩展小部件

jQuery UI 的部件工厂使得构建扩展现有部件功能的部件变得容易。这样做可以让你在现有基础上构建强大的部件,也可以对现有部件的功能进行微调。

注意:本文假设你对部件工厂是什么以及它如何工作有一些基本了解。如果你不熟悉这一点,请先阅读如何使用部件工厂

链接 创建部件扩展

使用部件工厂创建部件是通过将部件名称和原型对象传递给 $.widget() 来完成的。以下代码在 "custom" 命名空间中创建了一个 "superDialog" 部件。

1
$.widget( "custom.superDialog", {} );

为了允许扩展,$.widget() 可选地接受一个部件的构造函数作为父级。当指定父部件时,将其作为第二个参数传递——在部件名称之后,在部件原型对象之前。

与上一个示例一样,以下代码也在 "custom" 命名空间中创建了一个 "superDialog" 部件。然而,这次传递了 jQuery UI 对话框部件$.ui.dialog)的构造函数,表明 superDialog 部件应该使用 jQuery UI 的对话框部件作为父级。

1
$.widget( "custom.superDialog", $.ui.dialog, {} );

在这里,superDialog 和 dialog 本质上是具有不同名称和命名空间的等效部件。为了使我们的新部件更有趣,我们可以向其原型对象添加方法。

部件的原型对象是传递给 $.widget() 的最后一个参数。到目前为止,我们的示例一直使用空对象。让我们向该对象添加一个方法

1
2
3
4
5
6
7
8
9
10
$.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" );

现在 superDialog 有一个 red() 方法,它会将其文本颜色更改为红色。请注意部件工厂如何自动将 this 设置为部件的实例对象。有关实例上可用方法和属性的完整列表,请参阅部件工厂的 API 文档

链接 扩展现有方法

有时你需要调整或添加到现有部件方法的行为。为此,请在原型对象上指定一个与要覆盖的方法同名的方法。以下示例覆盖了对话框的 open() 方法。由于对话框默认会自动打开,因此当此代码运行时,将记录 "open"

1
2
3
4
5
6
7
8
$.widget( "custom.superDialog", $.ui.dialog, {
open: function() {
console.log( "open" );
}
});
// Create a new <div>, and convert it into a superDialog.
$( "<div>" ).superDialog();

虽然它运行了,但有一个问题。由于我们覆盖了 open() 的默认行为,对话框不再显示在屏幕上。

当我们将方法放在原型对象上时,我们实际上并没有覆盖原始方法——相反,我们将一个新方法放置在原型链中更高的位置。

为了使父级的方法可用,部件工厂提供了两种方法——_super()_superApply()

链接 使用 _super()_superApply() 访问父级

_super()_superApply() 调用父部件中同名的方法。请参考以下示例。与上一个示例一样,此示例也覆盖了 open() 方法以记录 "open"。然而,这次运行 _super() 来调用对话框的 open() 并打开对话框。

1
2
3
4
5
6
7
8
9
10
$.widget( "custom.superDialog", $.ui.dialog, {
open: function() {
console.log( "open" );
// Invoke the parent widget's open().
return this._super();
}
});
$( "<div>" ).superDialog();

_super()_superApply() 被设计成与原生 Function.prototype.call()Function.prototype.apply() 方法的行为相似。因此,_super() 接受一个参数列表,而 _superApply() 接受一个参数数组。这种差异在下面的示例中显示。

1
2
3
4
5
6
7
8
9
$.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 );
}
});

链接 重新定义部件

jQuery UI 1.9 增加了部件重新定义自身的能力。因此,我们可以将现有部件的名称和构造函数传递给 $.widget(),而不是创建新部件。以下示例在 open() 中添加了相同的日志记录,但没有创建新部件来完成此操作。

1
2
3
4
5
6
7
8
$.widget( "ui.dialog", $.ui.dialog, {
open: function() {
console.log( "open" );
return this._super();
}
});
$( "<div>" ).dialog();

使用这种方法,您可以扩展现有部件的方法,并且仍然可以使用 _super() 访问原始方法——所有这些都无需创建新部件。

链接 部件与多态性

在与部件扩展及其插件交互时,有一点需要注意。父部件的插件不能用于在作为子部件的元素上调用方法。这在下面的示例中显示。

1
2
3
4
5
6
7
8
9
$.widget( "custom.superDialog", $.ui.dialog, {} );
var dialog = $( "<div>" ).superDialog();
// This works.
dialog.superDialog( "close" );
// This doesn't.
dialog.dialog( "close" );

上面,父部件的插件 dialog() 不能在作为 superDialog 的元素上调用 close() 方法。有关调用部件方法的更多信息,请参阅部件方法调用

链接 自定义单个实例

到目前为止,我们看到的所有示例都扩展了部件原型上的方法。在原型上覆盖的方法会影响部件的所有实例。

为了说明这一点,请参考下面的示例;对话框的两个实例都使用相同的 open() 方法。

1
2
3
4
5
6
7
8
9
10
$.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();

虽然这很强大,但有时你只需要更改部件单个实例的行为。为此,请获取实例的引用并使用正常的 JavaScript 属性赋值来覆盖方法。下面的示例显示了这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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" );

这种为单个实例覆盖方法的技术非常适合一次性定制。