使用jQueryUI的widget来写插件,相比于基本的jquery插件有一些好处:

* 方便实现继承,代码重用

* 默认是单例

* widget已经给你实现好的一些常用方法,例如destroy

带来好处的同时也带来了荆棘和陷阱,本文的目的就是梳理这些荆棘,标出哪里有陷阱。

基本知识:命名规范,public, private, this, this.element

如何开始写一个widget呢?模板如下:

(function ($) {
// utility functions (won’t be inherited)
function foo() {} $.widget('命名空间.插件名', $.继承插件的命名空间.插件名,{ /* snip */ });
})(jQuery);

其中命名空间是可选的,要不要继承别的widget也是可选的。大头是后面snip的部分,这也是下文要讲的。

一般来说工具函数写在widget外面比较合适,但如果你想要这些工具函数被子类继承,则需要写在widget里面。

写在widget里面的,就有public和private之分,规则是:

public方法首字符不是_

private方法首字符是_

当调用方法时,会先判断是否以_开头,如果是则不执行调用。

如果我非要在外面调用private方法,该怎么做?并非一点办法也没有:

var instance = $('<div>');
instance.mywidget('publicFunction'); // work
instance.mywidget('_privateFunction'); // silently fail
instance.data('mywidget')._privateFunction(); // work
$.mynamespace.mywidget.prototype._privateFunction(); // work
 

在widget内,this表示的是什么?我们在widget的一个public函数内用console.log(this)打出来瞧瞧:

日志显示,this是一个$.widget.$.(anonymous function).(anonymous function)

this.element是变成widget的那个jQuery对象,如果要用jquery的方法,往往首先要取到jquery对象。

this.options是插件的选项,下文会详解。

this.__proto__包含了插件中定义的所有public和private函数,以及继承过来的方法。

这里简单介绍一下__proto__:每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性 时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样 一直找下去,也就是我们平时所说的原型链的概念。

_create  _init    destroy

widget factory实现了一种单例模式,即不允许在同一个jQuery对象上多次实例化。

当调用$(XX).widgetName()进行初始化的时候,会执行以下代码(源码截取自jquery.ui.widget.js):

var instance = $.data( this, name ); // 从widget自身取出名字为name的数据
if ( instance ) {
instance.option( options || {} )._init(); // 若该数据已经存在则只调用_init
} else {
$.data( this, name, new object( options, this ) ); // 若数据还没有则新建一个实例,并将实例保存
}

当调用$(XX).widgetName(‘destroy’)进行销毁的时候,执行以下代码(源码截取自jquery.ui.widget.js):

this.element
.unbind( "." + this.widgetName )
.removeData( this.widgetName ); // 删除在create时保存的数据

有一个removeData的操作,那么下次调用$(XX).widgetName()就会重新实例化了。

需要注意的是,destroy方法在jquery.ui.widget.js中是有默认实现的,而_create和_init没有实现。因此如果用自己的方法覆盖destroy,不要忘记调用默认的:

destory: function () {
console.log('destory'); // call the original destroy method since we overwrote it
$.Widget.prototype.destroy.call(this);
}

以下示例代码验证_create和_init的区别以及destroy的作用:

var mw = $('#test').myWidget(); // _create  _init
mw = $('#test').myWidget(); // _init
mw.myWidget('destory');
mw = $('#test').myWidget(); // _create _init

那么在_create和_init以及destroy里分别应该做什么:

_create: 生成HTML,事件绑定。

_init: 执行默认的初始化动作,例如把页面变成初始状态。

destory: 调用$.Widget.prototype.destroy.call(this),删除HTML。

注意:绑定事件要注意给事件名加命名空间后缀:例如 .bind('mouseenter.mywidget', this._hover)

options

选项,在widget中的定义是options,而在调用时是option,注意定义的时候有s,调用的时候没s。

定义:

option

s

: {
field1: 'default',
function1: function () {
console.log('default option function1');
}
},

调用:

$('#test').mywidget('option', 'field1', 2);

widget默认实现了两个函数:_setOptions和_setOption,_setOptions的实现就是对每个要修改的option调用_setOption,也就是说真正修改的动作在_setOption里。因此,如果要重写_setOption函数,则一定不要忘记写:

$.Widget.prototype._setOption.apply(this, arguments);

_setOptions和_setOption这俩函数什么时候被调用呢?用下面这个例子来说明。

例如有这样的_setOption和_setOptions:

 
_setOption: function (key, value) {
console.log('_setOption: key=%s value=%s', key, value);
$.Widget.prototype._setOption.apply(this, arguments);
}, _setOptions: function (options) {
var key; console.group('_setOptions');
for (key in options) {
this._setOption(key, options[key]);
}
console.groupEnd(); return this;
},

以及一个打印options值的函数printOptions:

printOptions: function () {
console.group('options');
console.log('field1: %s', this.options.field1);
console.log('function1: %s', this.options.function1);
console.groupEnd();
},

我们像下面这样调用:

var instance = $('<div>');

// create widget with default options
console.group(); instance.mywidget();
instance.mywidget('printOptions'); console.groupEnd(); // create widget with specified options
instance.mywidget('destroy');
console.group(); var opts = {
field1: 'specified',
function1: function () {
console.log('specified option function1');
},
};
instance.mywidget(opts);
instance.mywidget('printOptions'); console.log('-------------');
instance.mywidget(opts); console.groupEnd(); // modify options
console.group(); instance.mywidget('option', 'field1', 2);
instance.mywidget('printOptions'); console.groupEnd();
 

打出的日志如下:

日志分为三大块。

第一块是不使用options来初始化,可以看到直接使用定义里默认的options,是不调用_setOption的。

第二块是使用options来初始化,这一块做了两个实验(日志中用--------将两块分隔),第一个实验是完全重建(_create, _init),从日志可以看到并没有调用_setOption;第二个实验只是重新初始化(_init),用的options都一样,从日志可以看到它调 用了_setOption,且在_init之前调用的。

第三块不是初始化,而仅仅是修改option值,可以清楚看到调用了_setOption。

何时会调用_setOption的结论:

1. 像instance.mywidget('option', 'field1', 2); 这样显式设置option时。

2. 带着options初始化时:

如果实例不存在,即需要调用_create,则不调用_setOption;

如果实例已存在,仅需要调用_init,则会在调用_init之前调用_setOption。

_trigger

注意这个_trigger是jQueryUI widget factory里的,和jQuery里$.fn命名空间下的trigger函数不是一个东西(后者不带下划线)。

_trigger一般用来回调用户传入options的callback。

在插件内部调用_trigger(‘myEvent’)即相当于调用options里面的myEvent这个回调函数。

要改动options里的event handler应该怎么做呢?不要使用bind/unbind,而是去修改options:

// bind (overwrite, not add event handler)
mw.myWidget('option', 'myEvent', function (event, ui) {
console.log('new implement');
});
// unbind
mw.myWidget('option', 'myEvent', null);

总结一下:

this._trigger(‘eventName’)是widget特有的,用于调用options里定义的callback。

this.element.trigger(‘eventName’)是jQuery的,可参考jQuery的事件的用法。(其中this.element是表示该插件的jQuery对象)

一个_trigger的样例:

// 模板
this._trigger( "callbackName" , [eventObject], [uiObject] )

callbackNameThe name of the event you want to dispatcheventObject(Optional)An (mocked) event object. _trigger wraps this object and stores it in event.originalEventThe user receives an object with event.type == this.widgetEventPrefix + "eventname"uiObject(Optional)An object containing useful properties the user may need to access.Protip: Use a method like ._uito generate objects with a consistent schema.

// 调用样例
this._trigger( "hover", e /* e.type == "mouseenter" */, { hovered: $(e.target)});
// The user can subscribe using an init option
$("#elem").filterable( { hover: function(e,ui) { } } );
// Or with traditional event binding/delegation
$("#elem").bind( "filterablehover" , function(e,ui) { } );

jquery-ui-widget的更多相关文章

  1. Jquery ui widget开发

    Jquery ui 提供了一些基本的widget,但是他提供了很好的机制来创建widget.在jquery css framework中包含了基本的css样式(视觉和感觉诸如颜色,字体大小,图标等), ...

  2. 使用 jQuery UI Widget Factory 编写有状态的插件(Stateful Plugins)

    使用 jQuery UI Widget Factory 编写有状态的插件(Stateful Plugins) Note 这一章节的内容是基于 Scott Gonzalez 一篇博客 Building ...

  3. JQuery UI Widget Factory官方Demo

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  4. jQuery UI Widget(1.8.1)工作原理--转载

    先看下代码的相关注释: /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/abo ...

  5. jQuery UI Widget 原理

    先看下代码的相关注释: /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/abo ...

  6. 通过扩展jQuery UI Widget Factory实现手动调整Accordion高度

    □ 实现Accordion高度一致 <head> <meta name="viewport" content="width=device-width&q ...

  7. jquery ui widget 源代码分析

    jquery ui 的全部组件都是基于一个简单,可重用的widget. 这个widget是jquery ui的核心部分,有用它能实现一致的API.创建有状态的插件,而无需关心插件的内部转换. $.wi ...

  8. jQuery UI Widget(1.8.1)工作原理

    /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) * Dual l ...

  9. jQuery UI Widget Factory

    https://learn.jquery.com/jquery-ui/widget-factory/ The jQuery UI Widget Factory is an extensible bas ...

  10. jquery.ui.widget详解

    案例详解 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...

随机推荐

  1. Android根据Button状态(normal,focused,pressed)显示不同背景图片

    Android根据Button状态(normal,focused,pressed)显示不同背景图片 Android中Button 有focused, selected, pressed 等不同状态,通 ...

  2. AngularJS 从零开始学习(一)

    什么是AngularJS? AngularJS是一个把HTML(视图)绑定到JavaScript对象(模型)上的框架.当模型改变时,页面也能自动随之更新,反之亦然.当某个域的内容发生变化时,与之关联的 ...

  3. 利用Hierarchy Viewer优化布局

    好久没更新博客了,趁着清明来写点什么. 今天来讲下如何使用android中提供的工具优化我们的布局.首先我们写一个最简单的框架布局. <?xml version="1.0" ...

  4. C++:预处理指令

    Preprocessor directives 预处理器指令 预处理器指令是指那些包含在我们代码中的预处理器语句行,这些预处理器语句不是真正的代码语句,但是他们指导程序如何进行编译.这些语句总是以 ‘ ...

  5. Chapter 1 First Sight——18

    But at least he sent me to an empty desk at the back without introducing me to the class. 但是最后他给我最后面 ...

  6. PAT1015

    A reversible prime in any number system is a prime whose "reverse" in that number system i ...

  7. Java 学习路线以及各阶段学习书籍,博文,视频的分享

    感谢: 感谢每一个打开这篇文章的人,听我在这里瞎扯!至于我为什么会有闲情写这篇文章呢?因为我每天想的是为什么要给我这样的需求,背后的人性是什么,我能再做些什么能让他更好.久而久之,我也稍微有了些自己的 ...

  8. What is “Mock You” :Raise,callback,verify [转载]

    http://www.cnblogs.com/wJiang/archive/2010/02/21/1670637.html Raise 如果你说会用Setup,那么Raise就更简单了.这里注意下它是 ...

  9. 解决Android Studio Gradle Build特别慢的问题

    解决Android Studio Gradle Build 特别慢的问题 C:\Users\你的用户名\.gradle目录下新建一个文件名为gradle.properties的文件.内容为:org.g ...

  10. Java对数函数及Java对数运算

    Java对数函数及Java对数运算 2010-05-17 10:32 中国IT实验室 佚名   关键字:Java   Java对数函数的计算方法非常有问题,然而在API中却有惊人的误差.但是假如运用了 ...