jQuery UI 如何使用部件庫


我們將建立一個進度條。如下面實例所示,這可以透過呼叫 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 外掛保留。當創建您自己的插件時,您應該創建自己的命名空間。這樣才能更清楚插件來自哪裡,屬於哪個範圍。