像jQuery提供 fn.extend() 方法从而可以简单地创建插件一样,jQuery UI也提供了机制使得创造插件变得简单,也确保了公共API功能在新的插件中被保留。
1.首先,创建一个名为  jquery.ui.calculator.js 的文件,代码如下:
 1 (function($) {  2  $.widget("ui.calculator", {  3  options: {  4  autoShow: true,  5  currentSum: []  6  },  7  _create: function() {  8  },  9  destroy: function() { 10  }, 11  disable: function() { 12  }, 13  enable: function() { 14  }, 15  }); 16 })(jQuery);
 
我们通过 $.widget() 方法定义我们的插件。这个方法接受两个参数。第一个参数定义该插件的名称,此名称需以 ui 命名空间开头。第二个参数是可选属性,表示插件的功能,包含属性和方法。
此处,我们有两个配置选项。
autoShow定义当页面加载时,是否播放。
currentSum是一个空数组。
_create用来添加初始化函数,
destrory,disable,enable方法是公共API,我们总是需要定义这些方法。
2. _create函数代码如下
 1 _create: function() {  2  var div = $("<div />"),  3  list = $("<ul></ul>", {  4  "class": "ui-helper-reset ui-helper-clearfix"  5  }),  6  li = $("<li />", {  7  "class": "ui-corner-all ui-state-default"  8  }),  9  a = $("<a />", { 10  href: "#", 11  "class": "ui-calculator-button" 12  }), 13  container = div.clone() 14  .addClass("ui-calculator-container ui-corner-all ui-widget- 15  content ui-helper-clearfix"), 16 display = div.clone() 17  .addClass("ui-corner-all ui-widget-content ui-calculator- 18  display").text("0").appendTo(container), 19  numberpad = div.clone() 20  .addClass("ui-calculator-numberpad").appendTo(container), 21  functionpad = div.clone() 22  .addClass("ui-calculator-functionpad").appendTo(container), 23  numberlist = list.clone().appendTo(numberpad), 24  functionlist = list.clone().appendTo(functionpad), 25  buttons = ["","clear",7,8,9,4,5,6,1,2,3,0,"."], 26  functions = ["/", "*", "-", "+", "="]; 27  for (var x = 0; x < buttons.length; x++) { 28  var listitem = li.clone().appendTo(numberlist), 29 linky = a.clone().text(buttons[x]).appendTo(listitem); 30 if(x === 0) { 31  $("<span />", { 32  "class": "ui-calculator-icon ui-icon ui-icon-arrowthick-1-w", 33  text: "Backspace" 34  }).appendTo(linky); 35  } else if (x === 1 || buttons[x] === 0) { 36  linky.addClass("ui-calculator-button-wide"); 37  } 38  } 39  for (var y = 0; y < functions.length; y++) { 40  var listitem2 = li.clone().addClass("ui-state-default") 41 .appendTo(functionlist), 42  linky2 = a.clone().text(functions[y]).appendTo(listitem2); 43 } 44  this.element 45  .addClass("ui-calculator ui-widget ui-helper-reset"); 46  (this.options.autoShow) ? 47  container.appendTo(this.element) : 48  container.appendTo(this.element).hide(); 49  this.element.this("li").bind({ 50  mouseenter: this._addHoverState, 51  mouseleave: this._removeHoverState, 52  click: this._buttonClick 53  }); 54 },
 
一开始我们定义了一些列的变量。前四个变量(div , ul , li , a)是我们创建用来构建控件必须的一系列元素。我们添加任何必须的每次基本元素上我们都要用到的一般属性。如 class,anchor,href.
接下来的六个变量是实际的元素,比用来构建控件,从基本元素克隆而来,并添加主题必须的class扩展他们。
我们使用for 循环在 buttonpad 容器中创建按钮容器。按钮由 <a> 构成,被包装在 <li> 中。他们中的一些按钮不是数字键,如回退,清除键。回退键有一个额外的 <span> ,用来显示 返回箭头 的图标。这是按钮数组中的第一个按钮。
清除键和 0 键,比其余的按键宽,我们知道清除label是按钮数组中的第二个按钮,所以我们能发现试用了数组索引1。 0 键是检查实际数组的值。这两个按键使用 ui-calculator-button-wide,其他按键上使用 ui-calculator-button。
我们使用另一个循环创建功能键,如除,乘等。将他们追加到第二个容器中。这时,没有一个按钮拥有附加元素或特殊的class,所以我们不需要 if 条件。
在函数中定义的this对象被局限于我们的插件。jQuery UI给我们的对象添加了两个特殊的属性,第一个是被称为 元素 ,指向触发插件方法的实际元素。第二个被称为 选项,参照 我们在插件一开始定义的属性配置对象。
当插件方法被触发时,我们通过 this.element 给元素添加一些 class 。这时我们检查 autoShow 选项,如果是默认值——true,我们只需附加我们的插件到页面。如果设为 false,我们依然附加它,但是立即 hide 它。
_create函数中做的最后一件事情,是附加一些 event handles 给 <li>。我们还没有 event handles 函数,添加后,我们能通过 this 对象访问他们。event handling 功能不能直接实现,我们用下划线做前缀。
3.公共API方法
 1 destroy: function() {  2  $.Widget.prototype.destroy.call(this, arguments);  3  this.element  4  .removeClass("ui-calculator ui-widget ui-helper-reset");  5  this.element.find("li").unbind();  6  this.element.children(":first").remove();  7 },  8 disable: function() {  9  $.Widget.prototype.disable.call(this, arguments); 10  this.element.find("li").unbind(); 11 }, 12 enable: function() { 13  $.Widget.prototype.enable.call(this, arguments); 14  this.element.find("li").bind({ 15  mouseenter: this._addHoverState, 16  mouseleave: this._removeHoverState, 17  click: this._buttonClick 18  }); 19 }, 20
 
所有这些功能遵循一个共同的格式,jQuery UI为我们做了重量级的。控件工厂已经定义了他们的方法,我们要做的是调用控件工厂使用JavaScript call() 函数定义的原始方法。原始方法能使用我们希望调用、附加给 $.Widget.prototype 的方法的名字,通过控件属性被访问。我们可以提供一些附加的代码。
在销毁功能中,我们移除了 class ,解除绑定了 event handlers ,移除了 DOM 结构。
在禁用功能中,我们再一次调用控件原始 禁用功能,然后简单地解除按钮上绑定的 over-states和 点击 。
在启用功能中,我们再一次调用控件工厂的原始启用方法,然后再一次添加我们的event handles。
3.添加自定义方法
我们提供了 autoShow选项,所以我们的控件可以附加到页面但不立即显示。我们需要提供一个在需要时,可以被用来显示控件的方法。
1 show: function() { 2  var el = this.element.children(":first"); 3  if (el.is(":hidden")) { 4  el.show(); 5  } 6  this._trigger("show", null, this.element); 7 },
 
我们的方法用一个键 show 存储着,所以开发者可以想使用我们已经定义的标准API方法
(如$(”#el”).calculator(“show”))一样使用它。在这个功能中我们需要做的是找到那个附在了控件的外层元素的第一个子元素,并在当前隐藏的情况下显示它。
一旦我们显示计算器,我们就出发了自定义事件。当开发者使用我们的控件hook关键的交互点时,这使得它变得很有用。 _trigger()方法接收三个参数:第一个用于触发事件,第二个用于原始浏览器事件对象,第三个是参照存储在 this.element中的元素。即使在自定义方法中,this 对象依然局限于我们的控件实例。注意这个 this 可以被任何我们想要传给 handler功能的开发者为我们自定义事件定义的 hash 键值对 。
4.在 _create()功能中,我们绑定了一些时间,如 mouseenter ,mouseleave ,click 。我们现在需要添加 handler 功能,当这些events 检测到被我们的空间检测到时,做出反应。
 1 _addHoverState: function() {  2  $(this).addClass("ui-state-hover");  3 },  4 _removeHoverState: function() {  5  $(this).removeClass("ui-state-hover");  6 },  7 _buttonClick: function() {  8  var buttonText = $(this).text(),  9  display = $(".ui-calculator-display"), 10  newArray = $.ui.calculator.prototype.options.currentSum; 11  if (buttonText == "Backspace") { 12  if (display.text() !== "0" && display.text().length > 1) { 13  newArray.pop(); 14  $.ui.calculator.prototype.options.currentSum = newArray; 15  display.text(function(i, orig) { 16  return orig.substring(0, orig.length - 1); 17  }); 18  } 19  } else if (buttonText == "clear") { 20  $.ui.calculator.prototype.options.currentSum = []; 21  display.text("0"); 22  } else if (buttonText == "=") { 23  result = eval 24  ($.ui.calculator.prototype.options.currentSum.join("")); 25  display.text(result); 26  $.ui.calculator.prototype.options.currentSum = [result]; 27  } else if (buttonText == "/" || buttonText == "*" || buttonText 28  == "-" || buttonText == "+") { 29  $.ui.calculator.prototype.options.currentSum.push(buttonText); 30  display.text(buttonText); 31  } else { 32  $.ui.calculator.prototype.options.currentSum.push(buttonText); 33  if (display.text() == "0" || display.text() == "/" || 34  display.text() == "*" || display.text() == "-" || 35  display.text() == "+") { 36  display.text(""); 37  } 38  display.text(function(i, orig) { 39  return orig + buttonText; 40  }); 41  } 42 }
 
当mouseenter和mouseleae事件被检测到,这个功能被调用。我们仅仅添加和移除适合的状态class ,这样 悬停状态被应用并分别被移除。
点击 handler 有点复杂,但是所有我们做的,是检查按钮被点击,并且为不同类型的按钮不同方式的反应。
在单击 handler中,我们做的第一件事,是存储一些变量,包括被点击按钮的文本,控件的播放元素,currentSum 配置选项的备份。因为 this 对象不再局限于我们的控件实例,我们不能使用this.options访问这个选项,但是我们依然可以通过使用$.ui.calculator.prototype.options这个控件属性访问他们。
我们这时有一个if 声明,检查每个按钮的类型,包括 backspace ,clear ,= ,/,+等功能键和数字键。
如果回退键 被点击,我们检查 显示 不是仅仅由 0 构成,并且将要显示的文本长度比一个字节长。如果条件,我们将控件属性中获得的 currentSum 移除最后一个项目。使用新的短数组更新currentSum,并且这时从将要显示的文本中移除最后一个字符。
如果清除键被点击,我们仅仅通过设置currentSum选项的值为空数组来清空它,并且将显示块重设为0。
如果 = 键被点击,我们估计存储在currentSum数组中的表达式,在显示元素中显示结果,并在这时更新currentSum,以使它近包含结果。
如果任何其他功能键被点击,我们仅仅添加按键上的文本到currentSum数组,并将显示元素的文本为被点击按键的文本。
最后,如果任何数字键被点击,我们首先检查当前将要显示在计算器上的是什么。如果它是0或功能键,我们清空显示元素。我们这时用心数字更新显示。这时如果一个数字已经被点击,这时显示向新数字一样被更新为包含原始数字。但是如果功能键被按下,数字像一个新数字 +1。
5.控件的样式表
 1 .ui-calculator { width:9.65em; }  2 .ui-calculator-container { padding:.2em .2em 0; }  3 .ui-calculator-display {  4  width:12.3em; padding:.3em; margin-bottom:.3em;  5  font-size:0.7em; overflow:auto;  6 }  7 .ui-calculator-numberpad {  8  width:7em; float:left;  9 } 10 .ui-calculator-functionpad { width:2em; float:left; } 11 .ui-calculator li { 12  float:left; margin:0 .2em .2em 0; 13 } 14 .ui-calculator-button { 15  display:block; width:2em; height:1.2em; 16  padding:.3em 0 .5em; text-align:center; border:0; 17 } 18 .ui-calculator-button-wide { width:4.3em; } 19 .ui-calculator-icon { margin:.2em auto; }
 
  jquery.ui.calculator.css
6.使用控件
   1:  <!DOCTYPE html>
   2:  <html>
   3:    <head>
   4:    <meta charset="utf-8">
   5:    <title>Widget Factory - calculator
   6:    </title>
   7:    <link rel="stylesheet"  
   8:      href="css/smoothness/jquery-ui-1.8.9.custom.css">
   9:    <link rel="stylesheet"   href="css/jquery.ui.claculator.css">
  10:    </head>
  11:    <body>
  12:      <div id="calc">
  13:      </div>
  14:      <script src="development-bundle/jquery-1.4.4.js">
  15:      </script>
  16:      <script src="development-bundle/ui/jquery.ui.core.js">
  17:      </script>
  18:      <script  
  19:        src="development-bundle/ui/jquery.ui.widget.js">
  20:      </script>
  21:      <script src="js/jquery.ui.calculator.js">
  22:      </script>
  23:      <script>
  24:        (function($) {
  25:          $("#calc").calculator({
  26:            autoShow: false,
  27:            show: function(e, ui) {
  28:              alert(e + ", " + $(ui).attr("id");
  29:            }
  30:          });
  31:        })(jQuery);
  32:      </script>
  33:    </body>
  34:  </html>
 
calculator.html