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 的事件。