在本節(jié)中,我們將介紹jQuery UI如何使用部件庫(kù)。
在下述的示例中,我們將創(chuàng)建一個(gè)進(jìn)度條,這可以通過(guò)調(diào)用jQuery.widget()
來(lái)完成,它帶有兩個(gè)參數(shù):一個(gè)是要?jiǎng)?chuàng)建的插件名稱,一個(gè)是包含支持插件的函數(shù)的對(duì)象文字。當(dāng)插件被調(diào)用時(shí),它將創(chuàng)建一個(gè)新的插件實(shí)例,所有的函數(shù)都將在該實(shí)例的語(yǔ)境中被執(zhí)行。這與兩種重要方式的標(biāo)準(zhǔn)jQuery插件不同。首先,語(yǔ)境是一個(gè)對(duì)象,不是DOM元素。其次,語(yǔ)境總是一個(gè)單一的對(duì)象,不是一個(gè)集合。
$.widget( "custom.progressbar", {
_create: function() {
var progress = this.options.value + "%";
this.element
.addClass( "progressbar" )
.text( progress );
}
});
插件的名稱必須包含命名空間,在這個(gè)實(shí)例中,我們使用了custom
命名空間。您只能創(chuàng)建一層深的命名空間,因此,custom.progressbar
是一個(gè)有效的插件名稱,而very.custom.progressbar
不是一個(gè)有效的插件名稱。
我們看到部件庫(kù)(Widget Factory)為我們提供了兩個(gè)屬性。this.element
是一個(gè)包含一個(gè)元素的jQuery對(duì)象。如果我們的插件在包含多個(gè)元素的jQuery對(duì)象上調(diào)用,則會(huì)為每個(gè)元素創(chuàng)建一個(gè)單獨(dú)的插件實(shí)例,且每個(gè)實(shí)例都會(huì)有自己的this.element
。第二個(gè)屬性,this.options
,是一個(gè)包含所有插件選項(xiàng)的鍵名/鍵值對(duì)的哈希(hash)。這些選項(xiàng)可以被傳給插件,如下所示:
$( "<div></div>" )
.appendTo( "body" )
.progressbar({ value: 20 });
當(dāng)我們調(diào)用jQuery.widget()
,它通過(guò)給jQuery.fn
(用于創(chuàng)建標(biāo)準(zhǔn)插件的系統(tǒng)) 添加函數(shù)來(lái)擴(kuò)展jQuery。所添加的函數(shù)名稱是基于您傳給jQuery.widget()
的名稱,不帶命名空間-"progressbar"。傳給插件的選項(xiàng)是在插件實(shí)例中獲取設(shè)置的值。如下面的實(shí)例所示,我們可以為任意一個(gè)選項(xiàng)指定默認(rèn)值。當(dāng)設(shè)計(jì)您的API時(shí),您應(yīng)該清楚你的插件的最常見(jiàn)的使用情況,以便您可以設(shè)置適當(dāng)?shù)哪J(rèn)值,且確保使所有的選項(xiàng)真正可選。
$.widget( "custom.progressbar", {
// Default options.
options: {
value: 0
},
_create: function() {
var progress = this.options.value + "%";
this.element
.addClass( "progressbar" )
.text( progress );
}
});
現(xiàn)在我們可以初始化我們的進(jìn)度條,我們將通過(guò)在插件實(shí)例上調(diào)用方法來(lái)執(zhí)行動(dòng)作。為了定義一個(gè)插件方法,我們只在我們傳給jQuery.widget()
的對(duì)象中引用函數(shù)。我們也可以通過(guò)給函數(shù)名加下劃線前綴來(lái)定義"private"方法。
$.widget( "custom.progressbar", {
options: {
value: 0
},
_create: function() {
var progress = this.options.value + "%";
this.element
.addClass( "progressbar" )
.text( progress );
},
// Create a public method.
value: function( value ) {
// No value passed, act as a getter.
if ( value === undefined ) {
return this.options.value;
}
// Value passed, act as a setter.
this.options.value = this._constrain( value );
var progress = this.options.value + "%";
this.element.text( progress );
},
// Create a private method.
_constrain: function( value ) {
if ( value > 100 ) {
value = 100;
}
if ( value < 0 ) {
value = 0; }
return value;
}
});
為了在插件實(shí)例上調(diào)用方法,您可以向jQuery插件傳遞方法的名稱。如果您調(diào)用的方法接受參數(shù),您只需簡(jiǎn)單地在方法名后面?zhèn)鬟f這些參數(shù)即可。
注意:通過(guò)向同一個(gè)用于初始化插件的jQuery函數(shù)傳遞方法名來(lái)執(zhí)行方法。這樣做是為了在保持鏈方法調(diào)用時(shí)防止jQuery命名空間污染。在本章節(jié)的后續(xù),我們將看到看起來(lái)更自然的其他用法。
var bar = $( "<div></div>" )
.appendTo( "body" )
.progressbar({ value: 20 });
// Get the current value.
alert( bar.progressbar( "value" ) );
// Update the value.
bar.progressbar( "value", 50 );
// Get the current value again.
alert( bar.progressbar( "value" ) );
option()
方法是自動(dòng)提供給插件的。option()
方法允許您在初始化后獲取并設(shè)置選項(xiàng)。該方法像jQuery的.css()
和.attr()
方法:您可以只傳遞一個(gè)名稱作為取值器來(lái)使用,也可以傳遞一個(gè)名稱和值作為設(shè)置器使用,或者傳遞一個(gè)鍵名/鍵值對(duì)的哈希來(lái)設(shè)置多個(gè)值。當(dāng)作為取值器使用時(shí),插件將返回與傳入名稱相對(duì)應(yīng)的選項(xiàng)的當(dāng)前值。當(dāng)作為設(shè)置器使用時(shí),插件的_setOption
方法將被每個(gè)被設(shè)置的選項(xiàng)調(diào)用。我們可以在我們的插件中指定一個(gè)_setOption
方法來(lái)反應(yīng)選項(xiàng)更改。對(duì)于更改選項(xiàng)要獨(dú)立執(zhí)行的動(dòng)作,我們可以重載_setOptions
。
$.widget( "custom.progressbar", {
options: {
value: 0
},
_create: function() {
this.options.value = this._constrain(this.options.value);
this.element.addClass( "progressbar" );
this.refresh();
},
_setOption: function( key, value ) {
if ( key === "value" ) {
value = this._constrain( value );
}
this._super( key, value );
},
_setOptions: function( options ) {
this._super( options );
this.refresh();
},
refresh: function() {
var progress = this.options.value + "%";
this.element.text( progress );
},
_constrain: function( value ) {
if ( value > 100 ) {
value = 100;
}
if ( value < 0 ) {
value = 0;
}
return value;
}
});
最簡(jiǎn)單的擴(kuò)展插件的方法是添加回調(diào),這樣用戶就可以在插件狀態(tài)發(fā)生變化時(shí)做出反應(yīng)。我們可以看下面的實(shí)例如何在進(jìn)度達(dá)到100%時(shí)添加回調(diào)到進(jìn)度條。_trigger()
方法有三個(gè)參數(shù):回調(diào)名稱,一個(gè)啟動(dòng)回調(diào)的jQuery事件對(duì)象,以及一個(gè)與事件相關(guān)的數(shù)據(jù)哈希。回調(diào)名稱是唯一一個(gè)必需的參數(shù),但是對(duì)于想要在插件上實(shí)現(xiàn)自定義功能的用戶,其他的參數(shù)是非常有用的。例如,如果我們創(chuàng)建一個(gè)可拖拽插件,我們可以在觸發(fā)拖拽回調(diào)時(shí)傳遞mousemove事件,這將允許用戶對(duì)基于由事件對(duì)象提供的x/y坐標(biāo)上的拖拽做出反應(yīng)。請(qǐng)注意,傳遞到_trigger()
的原始的事件必須是一個(gè)jQuery事件,而不是一個(gè)原生的瀏覽器事件。
$.widget( "custom.progressbar", {
options: {
value: 0
},
_create: function() {
this.options.value = this._constrain(this.options.value);
this.element.addClass( "progressbar" );
this.refresh();
},
_setOption: function( key, value ) {
if ( key === "value" ) {
value = this._constrain( value );
}
this._super( key, value );
},
_setOptions: function( options ) {
this._super( options );
this.refresh();
},
refresh: function() {
var progress = this.options.value + "%";
this.element.text( progress );
if ( this.options.value == 100 ) {
this._trigger( "complete", null, { value: 100 } );
}
},
_constrain: function( value ) {
if ( value > 100 ) {
value = 100;
}
if ( value < 0 ) {
value = 0;
}
return value;
}
});
回調(diào)函數(shù)本質(zhì)上只是附加選項(xiàng),所以您可以像其他選項(xiàng)一樣獲取并設(shè)置它們。無(wú)論何時(shí)執(zhí)行回調(diào),都會(huì)有一個(gè)相對(duì)應(yīng)的事件被觸發(fā)。事件類型是通過(guò)連接插件的名稱和回調(diào)函數(shù)名稱確定的?;卣{(diào)和事件都接受兩個(gè)相同的參數(shù):一個(gè)事件對(duì)象和一個(gè)與事件相關(guān)的數(shù)據(jù)哈希,具體如下面實(shí)例所示。您的插件可能需要包含防止用戶使用的功能,為了做到這點(diǎn),最好的方法就是創(chuàng)建可撤銷(xiāo)的回調(diào)。用戶可以撤銷(xiāo)回調(diào)或者相關(guān)的事件,與他們撤銷(xiāo)任何一個(gè)原生事件一樣,都是通過(guò)調(diào)用 event.preventDefault()
或返回false
來(lái)實(shí)現(xiàn)的。如果用戶撤銷(xiāo)回調(diào),_trigger()
方法將返回false
,這樣您就能在插件內(nèi)實(shí)現(xiàn)合適的功能。
var bar = $( "<div></div>" )
.appendTo( "body" )
.progressbar({
complete: function( event, data ) {
alert( "Callbacks are great!" );
}
})
.bind( "progressbarcomplete", function( event, data ) {
alert( "Events bubble and support many handlers for extreme flexibility." );
alert( "The progress bar value is " + data.value );
});
bar.progressbar( "option", "value", 100 );
現(xiàn)在我們已經(jīng)看到如何使用部件庫(kù)(Widget Factory)創(chuàng)建一個(gè)插件,接下來(lái)讓我們看看它實(shí)際上是如何工作的。當(dāng)您調(diào)用jQuery.widget()
時(shí),它將為插件創(chuàng)建一個(gè)構(gòu)造函數(shù),并設(shè)置您為插件實(shí)例傳入的作為原型的對(duì)象。所有自動(dòng)添加到插件的功能都來(lái)自一個(gè)基本的小部件原型,該原型定義為jQuery.Widget.prototype
。當(dāng)創(chuàng)建插件實(shí)例時(shí),會(huì)使用jQuery.data
把它存儲(chǔ)在原始的DOM元素上,插件名作為鍵名。
由于插件實(shí)例直接鏈接到DOM元素上,您可以直接訪問(wèn)插件實(shí)例,而不需要遍歷插件方法。這將允許您直接在插件實(shí)例上調(diào)用方法,而不需要傳遞字符串形式的方法名,同時(shí)您也可以直接訪問(wèn)插件的屬性。
var bar = $( "<div></div>" )
.appendTo( "body" )
.progressbar()
.data( "progressbar" );
// Call a method directly on the plugin instance.
bar.option( "value", 50 );
// Access properties on the plugin instance.
alert( bar.options.value );
您也可以在不遍歷插件方法的情況下創(chuàng)建一個(gè)實(shí)例,通過(guò)選項(xiàng)和元素直接調(diào)用構(gòu)造函數(shù)即可:
var bar = $.custom.progressbar( {}, $( "<div></div>" ).appendTo( "body") );
// Same result as before.
alert( bar.options.value );
插件有構(gòu)造函數(shù)和原型的最大好處是易于擴(kuò)展插件。通過(guò)添加或修改插件原型上的方法,我們可以修改插件所有實(shí)例的行為。例如,如果我們想要向進(jìn)度條添加一個(gè)方法來(lái)重置進(jìn)度為0%,我們可以向原型添加這個(gè)方法,它將在所有插件實(shí)例上可調(diào)用。
$.custom.progressbar.prototype.reset = function() {
this._setOption( "value", 0 );
};
如需了解擴(kuò)展小部件的更多細(xì)節(jié),以及如何在一個(gè)已有的小部件上創(chuàng)建一個(gè)全新的小部件的更多細(xì)節(jié),請(qǐng)查看通過(guò)部件庫(kù)(Widget Factory)擴(kuò)展小部件(Widget)。
在某些情況下,允許用戶應(yīng)用插件,然后再取消應(yīng)用。您可以通過(guò)_destroy()
方法做到這一點(diǎn)。在_destroy()
方法內(nèi),您應(yīng)該撤銷(xiāo)在初始化和后期使用期間插件所做的一切動(dòng)作。_destroy()
是通過(guò).destroy()
方法被調(diào)用的,.destroy()
方法是在插件實(shí)例綁定的元素從DOM上移除時(shí)被自動(dòng)調(diào)用的,所以這可被用于垃圾回收?;镜?code>.destroy()方法也處理一些常用的清理操作,比如從小部件的DOM元素上移除實(shí)例引用,從元素上解除綁定小部件命名空間中的所有事件,解除綁定所有使用_bind()
添加的事件。
$.widget( "custom.progressbar", {
options: {
value: 0
},
_create: function() {
this.options.value = this._constrain(this.options.value);
this.element.addClass( "progressbar" );
this.refresh();
},
_setOption: function( key, value ) {
if ( key === "value" ) {
value = this._constrain( value );
}
this._super( key, value );
},
_setOptions: function( options ) {
this._super( options );
this.refresh();
},
refresh: function() {
var progress = this.options.value + "%";
this.element.text( progress );
if ( this.options.value == 100 ) {
this._trigger( "complete", null, { value: 100 } );
}
},
_constrain: function( value ) {
if ( value > 100 ) {
value = 100;
}
if ( value < 0 ) {
value = 0;
}
return value;
},
_destroy: function() {
this.element
.removeClass( "progressbar" )
.text( "" );
}
});
部件庫(kù)(Widget Factory)只是創(chuàng)建有狀態(tài)插件的一種方式。這里還有一些其他不同的模型可以使用,且每個(gè)都有各自的優(yōu)勢(shì)和劣勢(shì)。部件庫(kù)(Widget Factory)解決了很多常見(jiàn)的問(wèn)題,且大大提高了效率,同時(shí)也大大提高了代碼的重用性,使它適合于jQuery UI及其他有狀態(tài)的插件。
請(qǐng)注意,在本章節(jié)中我們使用了custom
命名空間。ui
命名空間被官方的jQuery UI插件保留。當(dāng)創(chuàng)建您自己的插件時(shí),您應(yīng)該創(chuàng)建自己的命名空間。這樣才能更清楚插件來(lái)自哪里,屬于哪個(gè)范圍。
更多建議: