jQuery UI 如何使用部件庫(Widget Factory)
我們將創建一個進度條。正如下麵實例所示,這可以通過調用 jQuery.widget()
來完成,它帶有兩個參數:一個是要創建的插件名稱,一個是包含支持插件的函數的對象文字。當插件被調用時,它將創建一個新的插件實例,所有的函數都將在該實例的語境中被執行。這與兩種重要方式的標準 jQuery 插件不同。首先,語境是一個對象,不是 DOM 元素。其次,語境總是一個單一的對象,不是一個集合。
$.widget( "custom.progressbar", { _create: function() { var progress = this.options.value + "%"; this.element .addClass( "progressbar" ) .text( progress ); } });
插件的名稱必須包含命名空間,在這個實例中,我們使用了 custom
命名空間。您只能創建一層深的命名空間,因此,custom.progressbar
是一個有效的插件名稱,而 very.custom.progressbar
不是一個有效的插件名稱。
我們看到部件庫(Widget Factory)為我們提供了兩個屬性。this.element
是一個包含一個元素的 jQuery 對象。如果我們的插件在包含多個元素的 jQuery 對象上調用,則會為每個元素創建一個單獨的插件實例,且每個實例都會有自己的 this.element
。第二個屬性,this.options
,是一個包含所有插件選項的鍵名/鍵值對的哈希(hash)。這些選項可以被傳給插件,如下所示:
$( "<div></div>" ) .appendTo( "body" ) .progressbar({ value: 20 });
當我們調用 jQuery.widget()
,它通過給 jQuery.fn
(用於創建標準插件的系統) 添加函數來擴展 jQuery。所添加的函數名稱是基於您傳給 jQuery.widget()
的名稱,不帶命名空間 - "progressbar"。傳給插件的選項是在插件實例中獲取設置的值。如下面的實例所示,我們可以為任意一個選項指定默認值。當設計您的 API 時,您應該清楚你的插件的最常見的使用情況,以便您可以設置適當的默認值,且確保使所有的選項真正可選。
$.widget( "custom.progressbar", { // Default options. options: { value: 0 }, _create: function() { var progress = this.options.value + "%"; this.element .addClass( "progressbar" ) .text( progress ); } });
調用插件方法
現在我們可以初始化我們的進度條,我們將通過在插件實例上調用方法來執行動作。為了定義一個插件方法,我們只在我們傳給 jQuery.widget()
的對象中引用函數。我們也可以通過給函數名加下劃線首碼來定義 "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; } });
為了在插件實例上調用方法,您可以向 jQuery 插件傳遞方法的名稱。如果您調用的方法接受參數,您只需簡單地在方法名後面傳遞這些參數即可。
注意:通過向同一個用於初始化插件的 jQuery 函數傳遞方法名來執行方法。這樣做是為了在保持鏈方法調用時防止 jQuery 命名空間污染。在本章節的後續,我們將看到看起來更自然的其他用法。
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()
方法是自動提供給插件的。option()
方法允許您在初始化後獲取並設置選項。該方法像 jQuery 的 .css()
和 .attr()
方法:您可以只傳遞一個名稱作為取值器來使用,也可以傳遞一個名稱和值作為設置器使用,或者傳遞一個鍵名/鍵值對的哈希來設置多個值。當作為取值器使用時,插件將返回與傳入名稱相對應的選項的當前值。當作為設置器使用時,插件的 _setOption
方法將被每個被設置的選項調用。我們可以在我們的插件中指定一個 _setOption
方法來反應選項更改。對於更改選項要獨立執行的動作,我們可以重載 _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; } });
添加回調
最簡單的擴展插件的方法是添加回調,這樣用戶就可以在插件狀態發生變化時做出反應。我們可以看下麵的實例如何在進度達到 100% 時添加回調到進度條。_trigger()
方法有三個參數:回調名稱,一個啟動回調的 jQuery 事件對象,以及一個與事件相關的數據哈希。回調名稱是唯一一個必需的參數,但是對於想要在插件上實現自定義功能的用戶,其他的參數是非常有用的。例如,如果我們創建一個可拖拽插件,我們可以在觸發拖拽回調時傳遞 mousemove 事件,這將允許用戶對基於由事件對象提供的 x/y 座標上的拖拽做出反應。請注意,傳遞到 _trigger()
的原始的事件必須是一個 jQuery 事件,而不是一個原生的流覽器事件。
$.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; } });
回調函數本質上只是附加選項,所以您可以像其他選項一樣獲取並設置它們。無論何時執行回調,都會有一個相對應的事件被觸發。事件類型是通過連接插件的名稱和回調函數名稱確定的。回調和事件都接受兩個相同的參數:一個事件對象和一個與事件相關的數據哈希,具體如下面實例所示。
您的插件可能需要包含防止用戶使用的功能,為了做到這點,最好的方法就是創建一個可撤銷的回調。用戶可以撤銷回調或者相關的事件,與他們撤銷任何一個原生事件一樣,都是通過調用 event.preventDefault()
或返回 false
來實現的。如果用戶撤銷回調,_trigger()
方法將返回 false
,這樣您就能在插件內實現合適的功能。
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 );
本質
現在我們已經看到如何使用部件庫(Widget Factory)創建一個插件,接下來讓我們看看它實際上是如何工作的。當您調用 jQuery.widget()
時,它將為插件創建一個構造函數,並設置您為插件實例傳入的作為原型的對象。所有自動添加到插件的功能都來自一個基本的小部件原型,該原型定義為 jQuery.Widget.prototype
。當創建插件實例時,會使用 jQuery.data
把它存儲在原始的 DOM 元素上,插件名作為鍵名。
由於插件實例直接鏈接到 DOM 元素上,您可以直接訪問插件實例,而不需要遍曆插件方法。這將允許您直接在插件實例上調用方法,而不需要傳遞字串形式的方法名,同時您也可以直接訪問插件的屬性。
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 );
您也可以在不遍曆插件方法的情況下創建一個實例,通過選項和元素直接調用構造函數即可:
var bar = $.custom.progressbar( {}, $( "<div></div>" ).appendTo( "body") ); // Same result as before. alert( bar.options.value );
擴展插件的原型
插件有構造函數和原型的最大好處是易於擴展插件。通過添加或修改插件原型上的方法,我們可以修改插件所有實例的行為。例如,如果我們想要向進度條添加一個方法來重置進度為 0%,我們可以向原型添加這個方法,它將在所有插件實例上可調用。
$.custom.progressbar.prototype.reset = function() { this._setOption( "value", 0 ); };
如需瞭解擴展小部件的更多細節,以及如何在一個已有的小部件上創建一個全新的小部件的更多細節,請查看 通過部件庫(Widget Factory)擴展小部件(Widget)。
清理
在某些情況下,允許用戶應用插件,然後再取消應用。您可以通過 _destroy()
方法做到這一點。在 _destroy()
方法內,您應該撤銷在初始化和後期使用期間插件所做的一切動作。_destroy()
是通過 .destroy()
方法被調用的,.destroy()
方法是在插件實例綁定的元素從 DOM 上移除時被自動調用的,所以這可被用於垃圾回收。基本的 .destroy()
方法也處理一些常用的清理操作,比如從小部件的 DOM 元素上移除實例引用,從元素上解除綁定小部件命名空間中的所有事件,解除綁定所有使用 _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( "" ); } });
關閉注釋
部件庫(Widget Factory)只是創建有狀態插件的一種方式。這裏還有一些其他不同的模型可以使用,且每個都有各自的優勢和劣勢。部件庫(Widget Factory)解決了很多常見的問題,且大大提高了效率,同時也大大提高了代碼的重用性,使它適合於 jQuery UI 及其他有狀態的插件。
請注意,在本章節中我們使用了 custom
命名空間。ui
命名空間被官方的 jQuery UI 插件保留。當創建您自己的插件時,您應該創建自己的命名空間。這樣才能更清楚插件來自哪里,屬於哪個範圍。