Widget Factory - Part 1
簡介
在 jQuery UI 中要撰寫一個視覺元件 (widget) 的模組, 在 jQuery UI v1.8 之前只能使用 plugin,自 jQuery UI v1.8 版之後則可以使用 Widget Factory 模組。該模組所提供的 APIs 在後繼的 v1.9, v1.10, v1.11 版都有少許的變更。這系列文章採用的是 v1.11 版的 APIs,這是寫文章時可供下載最新、最穩定的版本。使用 Widget Factory 時會需要用到兩個模組:core.js 和 widget.js;這兩個模組都存在於 jquery-ui.js 套件之下。所以必須載入 jquery-ui.js 或 jquery-ui.min.js。如果不會用到其它 jQuery 的介面元件,也不想載入整個 jquery-ui 的套件,就得將這兩個模組從 jquery-ui 套件中分離出來後,單獨載入。
這篇文章只解釋 Widget Factory 最重要的基礎部份,也就是只宣告一個 widget,完全沒有實作任何東西時,所宣告的 widget 會長成什麼樣。
使用 Widget Factory 宣告一個 widget 時所用的語法如下:
$.widget(name, [base], prototype);
- name
- 字串值。為此 widget 所取的名字,名字必須包含命名空間,例如: myNameSpace.myWidget。命名空間只能有一層,撰寫應用程式時,基本上是用不到這個命名空間的,但一定得給;因為 jquery-ui 會在 $ (這個 "$" 是 jQuery 在 script 中的簡寫)之中建立一個名為 myNameSapce 的物件,而在 $.myNameSpace 之下,又有個名為 myWidget 的物件,這就是宣告後 widget 所在的位置。所有自訂的 widget,只要命名空間相同,都會被放在一起;這樣比較好管理。同一個名命空間下的 widget 名稱就不能重複了。
- base
- 選項參數。此為所宣告之 widget 要繼承之模組名稱。例如: $.ui.button 或 $.ui.dialog。如果此參數從缺時,預設為繼承 $.Widget(注意:這裡 Widget 的 W 是大寫字母,表示它是一個物件類別的名稱)模組。
- prototype
- 賦予值為一函式 (function),在此函式中必須實作所有的繼承自 $.widget 的函式,這些函式包含供內部使用的函式 (private method),及供外部呼叫的函式 (public method)。供內部使用的函式,依慣例,在函式名稱都前置一個底線 "_",如: _create();供外部呼叫的函式則無前置底線,如:enable()。這種命名方式並非強制的規則,只是一般通用的慣例。
this.options & this.element
當執行完 $.widget() 之後,widget 的基本架構就構建完成了。此時,widget 己具有兩個很重要的屬性:options 及 element。在實作 widget 的各函式時,常會使用 this.options 及 this.element 去參考這兩個屬性。
- options
- 是個 Javascript 的物件,起始時預設內含兩個屬性:disabled 和 create。將來 widget 中要包含其它的屬性時,也都會放到這個物件裡。
- disabled
- 預設值為 false。當呼叫 .disable() 時,此屬性值會被改成 true。程式中可以偵測此屬性值以決定 widget 的狀態。
- create
- 預設值為 null。傳入值為一回呼函式 (callback function),當 widget 具體化 (instantiate) 時,會觸發一個型態為 create 的事件,同時將此回呼函式當成事件的處理函式執行。
- element
- 目前使用到的 jQuery 物件。在 jQuery 中,函式常常依序針對數個 jQuery 物件逐一處理,而 this.element 之中只含了一個 jQuery 的物件,也就是當前正在處理的物件。
Public Methods
假如宣告了一個名為 myWidget 的 widget。要呼叫 myWidget 中之函式的方式為 myWidget(methodName)
,methodName 是函式名稱的字串。這種方式是傳統 jQuery 呼叫函式的方式。在 v1.11 版後,還可以用 myWidget.("instance").methodName()
的方式來呼叫。以下是定義完 widget 之後,在還沒有實作任何函式時,就可以呼叫而不會抛出錯誤訊息的函式,因為完全沒實作,所以也做不了什麼事。各函式細節的部份,等實作時再做說明。
- myWidget.("option", optionName)
- myWidget.("option", optionName, value)
- myWidget.("option")
- myWidget.("option", object)
- 第一個呼叫方式讀取 optionName 的值,optionName 需為字串值。第二個呼叫方式設定 optionName 的值為 value。第三個呼叫方式讀取所有 option 的值,傳回值為一個物件,內含所有的 option 及其現值。第四個呼叫方式一次設定數個 option 的值,傳入值為一個物件,物件中所含有的 option,其值都會被重新設定為物件中的值。
- myWidget("widget")
- 傳回 jQuery 物件。這個函式不需要實作,就這樣用,其實這個函式只是把 this.element 傳回來而已。
- myWidget("instance")
- 傳回 widget 的 instance。這是唯一不能用 myWidget("instance").instance() 方式來呼叫的函式,這樣呼叫也沒什麼意義。這個函式也不需要實作,它主要的作用就在提供另一種呼叫 widget 中其它公用函式的方法罷了。
- myWidget("disable")
- Disable widget。此時會在 widget 的屬性 class 中加入 nameSpace-widgetName-disabled 這個字串。字串中的 nameSpace 及 widgetName 由 widget 的 nameSpace 及 widgetName 的字串值來取代。然後把
options.disabled
的值改成 true。 - myWidget("enable")
- Enable widget。將 widget 之 class 中的 nameSpace-widgetName-disabled 字串刪除;然後把
options.disabled
的值改成 false。 - myWidget("destroy")
- 清除 widget。
範例
範例中宣告了一個名為 prod.item 的 widget。並呼叫上述的各函式,大多數的結果都要到「開發者工具中」的主控台去看。只有 enable() 及 disable() 這兩個函式定義了 CSS 的類別,可以展示一點視覺效果。網頁下載後,過 2 秒會 disable widget,再過 2 秒會 enable widget,再過 3 秒會 destroy widget,因為只宣告了 widget 也沒實作,也沒什麼可以 destroy 的。
head
在 <head> 中記得要載入 jquery-ui.min.js。
CSS
<style> html { width: 100%; height: auto; } body { margin: 0 auto; width: 1000px; padding: 20px; } #item { width: 200px; height: 200px; border: 2px solid gray; background-color: skyblue; } .prod-item-disabled { opacity:0.3; } </style>
CSS 中用 #item{} 定義了元件的基本外觀。
.prod-item-disabled{} 定義了當元件被 disabled 時,將元件的 opacity 值降到 0.3。這樣在執行時會造成一點視覺效果。
HTML
<body> </body>
HTML 中定義了一個 id = "item" 的元件,作為 widget 的掛載點。
Script
<script> // widget created by widget factory, should put into another file (function($, undefined) { $.widget( "prod.item", { }); })(jQuery); $(function(){ console.log("jQuery version is " + $().jquery); console.log("jQuery-ui version is " + $.ui.version); console.log($.prod); var elem = $("#item").item(); console.log(elem.item("option")); console.log(elem.item("widget")); console.log(elem.item("instance")); setTimeout(function(){ elem.item("disable"); console.log(elem.item("option", "disabled")); // true }, 2000); setTimeout(function(){ elem.item("enable"); console.log(elem.item("option", "disabled")); // false }, 4000); setTimeout(function(){ elem.item("destroy"); }, 6000); }); </script>
第 3~7 行,定義了一名為 prod.item 的 widget。一般來說,這一部份的程式碼在實務上實作時,都會分開放在另一個檔案之中,以便維護。
第 11~12 行,在主控台中列印出使用的 jQuery 及 jQuery-ui 的版本。
第 14 行,在主控台中列印 $.prod 物件,所有以 prod 做為命名空間的 widget 都會放在這個物件之中。
第 18~20 行,依序呼叫三個函式,並在主控台印出傳回值。 可以比較一下 widget() 和 instance() 兩個函式的傳回值有何不同。
第 22~25 行,設定一個定時器,在網頁下載 2 秒過後,disable widget ,並將 options.disabled
的值印在主控台中。
第 26~29 行,設定一個定時器,在網頁下載 4 秒過後,enable widget ,並將 options.disabled
的值印在主控台中。
第 30~32 行,設定一個定時器,在網頁下載 6 秒過後,清除 widget。這個只是呼叫了 destroy() 這個函式,完全沒做任何事。