虽然大多数现有的 jQuery 插件都是无状态的,即我们对元素调用它们,而这正是我们与插件交互的全部内容,但有一大类功能不适合基本的插件模式。
为了填补这一空白,jQuery UI 实施了更高级的插件系统。新系统管理状态,允许通过单个插件公开多个函数,并提供各种扩展点。此系统称为小组件工厂,作为 jQuery UI 1.8 的一部分以 jQuery.widget
的形式公开;但是,它可以独立于 jQuery UI 使用。
为了演示小组件工厂的功能,我们将构建一个简单的进度条插件。
首先,我们将创建一个进度条,它只允许我们设置一次进度。正如我们在下面看到的,这是通过使用两个参数调用 jQuery.widget()
来完成的:要创建的插件的名称,以及包含函数以支持我们插件的对象字面量。
当我们的插件被调用时,它将创建一个新的插件实例,并且所有函数都将在该实例的上下文中执行。这与标准 jQuery 插件有两种重要的不同。首先,上下文是一个对象,而不是一个 DOM 元素。其次,上下文始终是一个单一对象,而不是一个集合。
使用 jQuery UI 小组件工厂的简单有状态插件
1
2
3
4
5
6
7
8
|
|
插件的名称必须包含一个命名空间,在本例中我们使用了 custom
命名空间。您只能创建一级深的命名空间,因此,custom.progressbar
是一个有效的插件名称,而 very.custom.progressbar
则不是。
注意:在我们的示例中,我们使用 custom
命名空间。ui
命名空间保留给官方 jQuery UI 插件。在构建自己的插件时,您应该创建自己的命名空间。这可以清楚地表明插件的来源以及它是否属于更大的集合。
我们还可以看到小部件工厂为我们提供了两个属性。this.element
是一个 jQuery 对象,只包含一个元素。如果我们的插件在包含多个元素的 jQuery 对象上被调用,那么将为每个元素创建一个单独的插件实例,并且每个实例将有其自己的 this.element
。第二个属性 this.options
是一个哈希,包含我们所有插件选项的键/值对。这些选项可以按此处所示传递给我们的插件。
向小部件传递选项
1
2
3
|
|
当我们调用 jQuery.widget()
时,它通过向 jQuery.fn
添加一个方法来扩展 jQuery(这与我们创建标准插件的方式相同)。它添加的功能的名称基于你传递给 jQuery.widget()
的名称,不带命名空间 - 在我们的示例中,它将创建 jQuery.fn.progressbar
。传递给我们的插件的选项是在我们插件实例中 this.options
中设置的值。
如下所示,我们可以为我们的任何选项指定默认值。在设计你的 API 时,你应该找出插件最常见的用例,以便你可以设置适当的默认值,并使所有选项真正可选。
为小部件设置默认选项
1
2
3
4
5
6
7
8
9
10
11
12
13
|
|
link 向小部件添加方法
现在我们已经可以初始化我们的进度条,我们将添加通过调用我们插件实例上的方法来执行操作的功能。要定义插件方法,我们只需在传递给 jQuery.widget()
的对象字面量中包含该函数。我们还可以通过在函数名称前加上下划线来定义“私有”方法。
创建小部件方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
|
要调用插件实例上的方法,请将方法的名称传递给 jQuery 插件。如果你要调用接受参数的方法,只需在方法名称后传递这些参数即可。
注意:通过将方法名称传递给用于初始化插件的相同 jQuery 函数来执行方法可能看起来很奇怪。这样做是为了防止污染 jQuery 命名空间,同时保持链接方法调用的能力。在本文后面,我们将看到可能感觉更自然的其他用途。
调用插件实例上的方法
1
2
3
4
5
6
7
8
9
10
11
12
|
|
link 使用选项
我们的插件自动提供的方法之一是 option()
方法。option()
方法允许您在初始化后获取和设置选项。此方法的工作方式与 jQuery 的 .css()
和 .attr()
方法完全相同:您可以只传递一个名称以将其用作 getter,传递一个名称和值以将其用作单个 setter,或传递一个名称/值对的哈希以设置多个值。当用作 getter 时,插件将返回与传入名称相对应的选项的当前值。当用作 setter 时,将为正在设置的每个选项调用插件的 _setOption
方法。我们可以在插件中指定一个 _setOption
方法来响应选项更改。对于独立于已更改选项数执行的操作,我们可以覆盖 _setOptions()
。
在设置选项时响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
|
link 添加回调
使您的插件可扩展的最简单方法之一是添加回调,以便用户可以在插件状态更改时做出反应。我们可以在下面看到如何向进度条添加回调,以表示进度已达到 100%。_trigger()
方法采用三个参数:回调的名称、引发回调的 jQuery 事件对象以及与事件相关的哈希数据。回调名称是唯一必需的参数,但对于希望在您的插件之上实现自定义功能的用户来说,其他参数非常有用。例如,如果我们正在构建一个可拖动插件,我们可以在触发拖动回调时传递 mousemove 事件;这将允许用户根据事件对象提供的 x/y 坐标对拖动做出反应。
请注意,传递给 _trigger()
的事件必须是 jQuery 事件,而不是原生浏览器事件。
为用户扩展提供回调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
|
回调函数本质上只是其他选项,因此您可以像获取和设置任何其他选项一样获取和设置它们。每当执行回调时,也会触发相应的事件。事件类型是通过连接插件名称和回调名称来确定的。回调和事件都接收相同的两个参数:事件对象和与事件相关的哈希数据,如下所示。
您的插件可能具有您希望允许用户阻止的功能。支持此功能的最佳方法是创建可取消的回调。用户可以通过调用 event.preventDefault()
或返回 false
来取消回调或其关联的事件,就像他们取消任何事件一样。如果用户取消回调,则 _trigger()
方法将返回 false
,以便您可以在插件中实现适当的功能。
绑定到小部件事件
1
2
3
4
5
6
7
8
9
10
11
12
13
|
|
link 深入了解
现在我们已经了解了如何使用小部件工厂构建插件,让我们来看看它实际是如何工作的。
当您调用jQuery.widget()
时,它会为您的插件创建一个构造函数,并将您传入的对象字面量设置为插件实例的原型。自动添加到插件的所有功能都来自基本小部件原型,该原型定义为jQuery.Widget.prototype
。当创建插件实例时,它将使用jQuery.data
存储在原始 DOM 元素上,其中插件的全名(插件的命名空间,加上连字符,加上插件的名称)作为键。
例如,jQuery UI 对话框小部件使用键"ui-dialog"
。
由于插件实例直接链接到 DOM 元素,因此您可以直接访问插件实例,而无需通过公开的插件方法(如果您愿意)。这将允许您直接在插件实例上调用方法,而不是将方法名作为字符串传递,并且还将使您可以直接访问插件的属性。
1
2
3
4
5
6
7
8
9
10
|
|
您还可以通过直接使用构造函数并使用选项和元素参数来创建实例,而无需通过插件方法
1
2
3
4
|
|
link 扩展插件的原型
拥有插件的构造函数和原型的最大好处之一是易于扩展插件。通过在插件的原型上添加或修改方法,我们可以修改插件所有实例的行为。例如,如果我们想为进度条添加一个方法以将进度重置为 0%,我们可以将此方法添加到原型中,它将立即可以在任何插件实例上调用。
1
2
3
|
|
有关扩展小部件的更多信息,包括如何在现有小部件之上构建全新小部件,请参阅使用小部件工厂扩展小部件。
link 清理
在某些情况下,允许用户应用然后取消应用你的插件是有意义的。你可以通过 _destroy()
方法实现这一点。在 _destroy()
方法中,你应该撤消你的插件在初始化或稍后使用期间可能执行的任何操作。_destroy()
由 destroy()
方法调用,如果与你的插件实例绑定的元素从 DOM 中移除,则会自动调用该方法,因此也可以将其用于垃圾回收。该基本 destroy()
方法还处理一些常规清理操作,例如从小部件的 DOM 元素中移除实例引用、取消绑定小部件命名空间中元素中的所有事件,以及取消绑定使用 _bind()
添加的所有事件。
向小部件添加销毁方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
|
链接 结束语
小部件工厂只是创建有状态插件的一种方式。有几种不同的模型可以使用,每种模型都有自己的优点和缺点。小部件工厂为你解决了大量常见问题,可以极大地提高生产力,它还极大地提高了代码重用性,使其非常适合 jQuery UI 以及许多其他有状态插件。