2016年4月28日 星期四

jQuery Widget Factory - part 3

Widget Factory - Part 3

簡介

這篇談 widget 有關事件觸發及事件處理的部份。首先,在 widget 進行具體化時,預設會觸發一個型態為 create 的事件,觸發的時機在執行完 _create() 之後,執行 _init() 之前。事件觸發後,widget 會先送一個事件給外部程式,然後去 this.options 物件裡找一個和事件型態名稱相同的屬性,以 create 這個事件為例,也就是屬性名為 create 的屬性,如果這個屬性的值正好是一個函式,這個函式就會被當成一個事件處理函式執行。如果找不到對應名稱的屬性,或對應名稱屬性的值不是函式,widget 就當這事件沒有必要處理,繼續往下執行。不只是預設觸發的 create 事件會這麼做,而是所有在 widget 內部觸發的事件,都會走完上述的這一趟程序。

當 widget 觸發一個事件,而將事件送到 widget 外部時,widget 會在訂定的事件型態名稱前面加上 widget 的名稱字串,然後將字串的字母都變成小寫。事件型態的名稱的字串中可以包含文、數字還有符號 "_" 及 "-"。例如: widget 的名稱為 "item",事件型態為 "create";此時,外部程式要監聽的事件名稱應為 "itemcreate"。

在 Widget Factory 中有三個跟觸發事件和處理事件相關的內建內部函式,這三個內部函式僅供呼叫使用,無需實作:

_trigger(type [, event ] [, data ])
觸發一個事件。
type
事件的型態名稱,賦予值為字串,字串中可以包含文、數字及 "_" 和 "-" 兩個符號。在這裡也可以用大寫字母,只是轉成事件型態名時,大寫字母都會被轉成小寫字母;為了避免麻煩,最好一律用小寫字母,反正還有兩個符號可以用來分隔字詞,還是看得清,讀得懂的。要記得在外部監聽 widget 的事件時,事件型態名稱前面還會加上 widget 的名稱,widget 的名稱中如果有大寫字母,也會被轉換成小寫字母。用中文取事件型態名稱也行,還真的可以成功運作,我有試過,感謝 UTF-8。
event
這是一個選項參數。如果是因為另一個事件的觸發才導致這個事件的觸發,可以將原事件的 event 參數,在此轉發出去給這個事件的處理函式,如果這是原始的觸發事件,就可以不給,但最好給個 null 值。
data
也是一個選項參數。賦予值是個 Javascript 的物件,可以透過這個物件傳送一些資料給事件處理函式。如果要給這個參數,前一個 event 參數就一定要給,至少要給 null 值。
_on([suppressDisabledCheck ] [, element ], handlers)
用來綁定事件和事件處理函式,綁定的事件可以由 widget 自行觸發,或是外部觸發。
suppressDisabledCheck
此為選項參數,賦予值為布林值,預設為 false。設定為 true 時,會先行檢查 element 是否處於 disabled 的狀態,如果不是,則執行事件處理函式,如果是,則不執行事件處理函式。
element
此為選項參數,賦予值為一個 jQuery 物件。不給時,預設為 this.element。給予時則指定派送事件給該物件。
handler
此為必要參數,參數一定得給;但使用的格式有點奇特。如果觸發事件的元件是 widget 的本身,則直接將事件型態名稱做為屬性名,屬性值為事件處理函式。倘若觸發事件的只是 widget 中的子元件,則屬性名的位置要放入一個字串,字串中以第一個空白字元做區分,第一個空白字元前為事件的型態名稱,第一個空白字元之後則全部都屬於 selector 的部份,語法和 CSS3 的選取器 (selector) 一樣,用來指定觸發事件的視覺元件,但選取的範圍侷限於 widget 的子輩元件。屬性值的部份依然是事件處理函式,這個要看範例程式會更清楚。
_off(element, eventName)
解除監聽事件。
element
為必要參數,指定要解除監聽的物件。賦予值為一個 jQuery 物件。
eventName
為必要參數,指定要解除監聽的事件型態。賦予值為一字串,字串中可包含多個事件型態的名稱,以空白字元分隔。

範例

範例中的視覺元件是兩個同心圓,點按內部的圓形,會改變內部圓形的顏色,點按外部的圓形,則不做任何反應。同心圓下有個按鈕,按下按鈕後,會解除內部圓形對 click 事件的監聽,之後再點按內部的圓形時,就不會有任何反應了。
記得開啟「開發者工具」中的「主控台」去查看輸出,可以對事件的處理時機有進一步的瞭解。

CSS

<style>
html {
  width:100%;
  height:auto;
}
body {
  margin:0 auto;
  width:1200px;
  padding-top:20px;
}
#item {
  width:200px;
  height:200px;
  border-radius: 50%;
  background-color:skyblue;
    text-align: center;
}
#itemCore {
    position: relative;
    top: 50%;
    left:50%;
    transform: translate(-50%, -50%);
    width:100px;
    height:100px;
    border-radius: 50%;
    background-color:lightgreen;  
}
</style>

CSS 中 #item{} 用來設定同心圓外部圓形的外觀, #itemCore{} 用來設定同心圓內部圓形的外觀,並讓內部圓形置於外部圓形的正中央。

HTML

<body>
    

</body>

HTML 中建立兩個 <div> 在流覧器中用圓形表示,還有一個按鈕。

Script

<script>
// widget created by widget factory
(function($, undefined) {
    $.widget( "prod.item", {
        version:"1.0.0",  
        options:{
            product:"Coffee",
            price:80,
            create:$.noop,  // default to no operation
            optionchanged:function(){
                console.log("\thandle optionchanged event in this.options");
            }
        },
        _create:function(){
            console.log("_create start");
            this._on({"click #itemCore":function(evt){
                console.log("\thandle a click event trigger by #itemCore");
                var elem = $("#itemCore")
                if(elem.hasClass("coral")){
                    elem.css("background-color", "lightgreen").removeClass("coral");
                } else {
                    elem.css("background-color", "coral").addClass("coral");
                }
            }});
            this._super();
            console.log("_create end");
        },
        _init:function(){
            console.log("_init start");
            this._super();
            console.log("_init end");
        },
        _setOption:function(key, value){
            this._super(key, value);
            console.log("_setOption");
            this._trigger("optionchanged", null, {key:key, value:value});
        },
        _setOptions:function(opts){
            this._super(opts);
        },
        _destroy:function(){
            this._super();
        },
        removeHandler:function(ent){
            this._off(this.element, "click");
        }
    });
})(jQuery);

  
$(function(){
    var elem = $("#item");
    // here use "item" + "create" as event type
    elem.bind("itemcreate", function(evt){
        console.log("\thandle create event outside of widget");
    });

    // use "item" + "optionchanged" as event type 
    elem.bind("itemoptionchanged", function(evt, data){
        console.log("\thandle optionchanged event outside of widget");
        console.log("\t%o", evt);
        console.log("\t%o", data);
    });

    $("#off").button().click(function(){
        $("#item").item("removeHandler");
    })
    // override create attribute in this.options
    elem.item({
        create:function(){
            console.log("\thandle create event in this.options");
        }
    });

    // trigger an "optionchanged"/"itemoptionchange" event
    elem.item("option", "price", 2.99);
});
</script>

第 6 行,指定 widget 的版本號碼,以便將來維護。
第 9 行,先定一個 create 屬性,預設值為 $.noop ,就是 no operation,不做任何事的意思。由 widget 在具體化時,由參數傳入來改寫參數值。
第 11~12 行,定義 optionchanged 事件的處理函式。
第 16~24 行,綁定由 #itemCore 所觸發的 click 事件,及事件的處理函式,注意第 16 行中,參數的寫法。
第 36 行,在呼叫 _setOption() 時觸發一個事件型態為 optionschanged 的事件,並變更後的屬性名及屬性值以 Javascript 物件的方式,當成參數傳出去。
第 44~47 行,定義一個名為 removeHandler 的外部函式,在函式中呼叫 this._off() 解除對 #itemCore 元件所觸發之 click 事件的監聽。
第 54~56 行,在 widget 外部綁定監聽 itemcreate 事件及其處理函式。
第 59~63 行,在 widget 外部綁定綁定監聽 itemoptionchanged 事件及其處理函式。
第 65~67 行,定義一個 jquery-ui 的按鈕,在觸發 click 事件時,於事件處理函式中呼叫 widget 中的 removeHandler 外部公用函式。,br> 第 69-73 行,具體化名為 item 的 widget,同時傳入一物件做為參數,物件中是對 create 事件的事件處理函式。
第 76 行,呼叫 widget 的 option 函式以觸發一個 optionchanged 的事件。

沒有留言:

張貼留言