/*!
* jQuery UI Widget 1.8.1
*
* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Widget
*/
(function( $ ) { var _remove = $.fn.remove; $.fn.remove = function( selector, keepData ) {
return this.each(function() {
if ( !keepData ) {
if ( !selector || $.filter( selector, [ this ] ).length ) {
$( "*", this ).add( this ).each(function() {
$( this ).triggerHandler( "remove" );
});
}
}
//dom元素在被删除前,触发一下remove事件,jquery框架本身没有对元素删除绑定事件
return _remove.call( $(this), selector, keepData );
});
}; $.widget = function( name, base, prototype ) {
var namespace = name.split( "." )[ 0 ],
fullName;
name = name.split( "." )[ 1 ];
fullName = namespace + "-" + name;
//比如ui.tab,上面的name='tab';fullName='ui-tab'; if ( !prototype ) {
prototype = base;
base = $.Widget;
}
//如果没有prototype,那么prototype就是base参数,实际base默认为$.Widget // create selector for plugin
$.expr[ ":" ][ fullName ] = function( elem ) {
return !!$.data( elem, name );
}; $[ namespace ] = $[ namespace ] || {};//是否有命名空间
$[ namespace ][ name ] = function( options, element ) {//根据上面的例子,即初始化了$.ui.tab=func
// allow instantiation without initializing for simple inheritance
if ( arguments.length ) {
this._createWidget( options, element );
}
}; var basePrototype = new base();//初始化,一般都是调用了new $.Widget()
// we need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
// $.each( basePrototype, function( key, val ) {
// if ( $.isPlainObject(val) ) {
// basePrototype[ key ] = $.extend( {}, val );
// }
// });
basePrototype.options = $.extend( {}, basePrototype.options );//初始化options值,注意不需要深度拷贝
$[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
namespace: namespace,
widgetName: name,
widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
widgetBaseClass: fullName
}, prototype );
//为新的ui模块创建原型,使用深度拷贝,在basePrototype上扩展一些模块基本信息,在扩展prototype,比如ui.tabs.js中就是tab的拥有各种方法的大对象 $.widget.bridge( name, $[ namespace ][ name ] );//将此方法挂在jQuery对象上
}; $.widget.bridge = function( name, object ) {
$.fn[ name ] = function( options ) {
var isMethodCall = typeof options === "string",
args = Array.prototype.slice.call( arguments, 1 ),
returnValue = this;
//如果第一个参数是string类型,就认为是调用模块方法
//剩下的参数作为方法的参数,后面会用到 // allow multiple hashes to be passed on init
options = !isMethodCall && args.length ?
$.extend.apply( null, [ true, options ].concat(args) ) :
options;
//可以简单认为是$.extend(true,options,args[0],...),args可以是一个参数或是数组 // prevent calls to internal methods
if ( isMethodCall && options.substring( 0, 1 ) === "_" ) {
return returnValue;
}
//开头带下划线的方法都是私有方法,不让调用 if ( isMethodCall ) {//如果是调用函数
this.each(function() {
var instance = $.data( this, name ),//得到实例,实例作为一个数据和元素关联上
methodValue = instance && $.isFunction( instance[options] ) ?
instance[ options ].apply( instance, args ) ://如果实例和方法均存在,调用方法,把args作为参数传进去
instance;//否则返回undefined
if ( methodValue !== instance && methodValue !== undefined ) {//如果methodValue不是jquery对象也不是undefined
returnValue = methodValue;
return false;//跳出each,一般获取options的值会走这个分支
}
});
} else {//不是函数调用的话
this.each(function() {
var instance = $.data( this, name );
if ( instance ) {//实例存在
if ( options ) {//有参数
instance.option( options );//调用option函数,一般是设置状态之类的操作
}
instance._init();//再次调用此函数,根据options调整
} else {
$.data( this, name, new object( options, this ) );
//没有实例的话,给元素绑定一个实例。注意这里的this是dom,object是模块类
}
});
} return returnValue;//返回,有可能是jquery对象,有可能是其他值
};
}; $.Widget = function( options, element ) {//所有模块的基类
// allow instantiation without initializing for simple inheritance
if ( arguments.length ) {//如果有参数,调用初始化函数
this._createWidget( options, element );
}
}; $.Widget.prototype = {
widgetName: "widget",
widgetEventPrefix: "",
options: {
disabled: false
},//上面的属性会在创建模块时被覆盖
_createWidget: function( options, element ) {
// $.widget.bridge stores the plugin instance, but we do it anyway
// so that it's stored even before the _create function runs
this.element = $( element ).data( this.widgetName, this );//缓存实例,保存jquery对象
this.options = $.extend( true, {},
this.options,
$.metadata && $.metadata.get( element )[ this.widgetName ],
options );//参数处理 var self = this;
this.element.bind( "remove." + this.widgetName, function() {
self.destroy();
});//注册销毁事件 this._create();//创建
this._init();//初始化
},
_create: function() {},
_init: function() {}, destroy: function() {//销毁模块:去除绑定事件、去除数据、去除样式、属性
this.element
.unbind( "." + this.widgetName )
.removeData( this.widgetName );
this.widget()
.unbind( "." + this.widgetName )
.removeAttr( "aria-disabled" )
.removeClass(
this.widgetBaseClass + "-disabled " +
"ui-state-disabled" );
}, widget: function() {//返回jquery对象
return this.element;
}, option: function( key, value ) {//设置选项函数
var options = key,
self = this; if ( arguments.length === 0 ) {
// don't return a reference to the internal hash
return $.extend( {}, self.options );//返回一个新的对象,不是内部数据的引用
} if (typeof key === "string" ) {
if ( value === undefined ) {
return this.options[ key ];//取值
}
options = {};
options[ key ] = value;//设置值
} $.each( options, function( key, value ) {
self._setOption( key, value );//调用内部的_setOption
}); return self;
},
_setOption: function( key, value ) {
this.options[ key ] = value; if ( key === "disabled" ) {//增加或是去除className
this.widget()
[ value ? "addClass" : "removeClass"](
this.widgetBaseClass + "-disabled" + " " +
"ui-state-disabled" )
.attr( "aria-disabled", value );
} return this;
}, enable: function() {
return this._setOption( "disabled", false );
},
disable: function() {
return this._setOption( "disabled", true );
}, _trigger: function( type, event, data ) {
var callback = this.options[ type ]; event = $.Event( event );
event.type = ( type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type ).toLowerCase();
data = data || {}; // copy original event properties over to the new event
// this would happen if we could call $.event.fix instead of $.Event
// but we don't have a way to force an event to be fixed multiple times
if ( event.originalEvent ) {//把原始的event属性重新赋到event变量上
for ( var i = $.event.props.length, prop; i; ) {
prop = $.event.props[ --i ];
event[ prop ] = event.originalEvent[ prop ];
}
} this.element.trigger( event, data ); return !( $.isFunction(callback) &&
callback.call( this.element[0], event, data ) === false ||
event.isDefaultPrevented() );
}
}; })( jQuery );

上面是jquery.ui.widget.js的源码,jquery ui的所有模块都是基于其中的widget方法进行扩展,使用统一的命名规范和编码风格。 
先来说一下原理: 
$.widget此函数完成了对jQuery本身的扩展,根据第一个参数来确定模块的命名空间和函数名;第二个参数确定模块的基类(默认是$.Widget);第三个参数实现模块本身的方法。比如标签切换插件jquery.ui.tabs.js中开始: 
$.widget(“ui.tabs”, {…});//这里只有两个参数,那么基类就默认是$.Widget 
第一个参数:”ui.tabs”用来表示在jQuery上选择(或增加)一个命名空间,即如果jQuery.ui不存在,则定义jQuery.ui = {},然后在jQuery.ui上增加一个函数,名称为tabs.最后调用$.widget.bridge将tabs方法挂在jQuery对象上。这样,所有的jquery对象将拥有tabs方法。

注意:jquery ui有严格的命名规范,每个控件对外只暴露一个借口。控件所有方法或属性通过向此借口传递不同参数来调用和获取。

jquery ui的大部分控件是基于$.Widget基类实现的。所以一般我们做控件是都要重写$.Widget类中的一些方法。一般来说,一个ui控件需要实现下列的方法或属性: 
属性: 
options 用来缓存控件各项参数 
私有方法,使用“$(xx).tabs(私有方法)”这种方式来调用私有方法时会立刻返回,调用不能成功: 
_create 控件初始化调用,多次调用$(xx).tabs()这样不带参数的方法只会执行一次 
_init 一般不用实现,默认为空函数,每次“$(xx).tabs()”这样调用时会调用此方法 
_setOption “$(xx).tabs(‘option’,xxx)”这种调用方式会调用此方法 
公开方法: 
destroy 销毁模块 
option 设置或获取参数 
enable 启用模块功能 
disable 禁用功能

几乎所有的jquery ui控件都会重写这些接口,同时增加控件相关的私有或公有方法。

记住,jquery ui的实例是和元素关联起来的,作为数据保存起来了。暴露给用户使用的只是jquery对象上增加的方法。一般我们不需要获取ui的实例。

转自:http://xj19891016.iteye.com/blog/789201#

jQuery UI Widget(1.8.1)工作原理的更多相关文章

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

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

  2. jQuery UI Widget 原理

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

  3. jquery ui widget 源代码分析

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

  4. Jquery ui widget开发

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

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

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

  6. JQuery UI Widget Factory官方Demo

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

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

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

  8. jQuery UI Widget Factory

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

  9. jquery.ui.widget详解

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

随机推荐

  1. Maratona Brasileira de Popcorn( 二分答案+暴力 )

    题意:输入三个数n,c,t . 桌子上有n堆爆米花,每一堆有ai个, 现在有c个人一起吃爆米花,每人每分钟最多能吃t个爆米花,但有两个规定:1.一堆爆米花只能一个人吃, 2.每个人只能吃连续的若干堆爆 ...

  2. Java Part 001( 02_01_理解面向对象 )

    Java完全支持面向对象的三个基本特征: 继承.封装.多态 面向对象的方式实际上由OOA(面向对象分析).OOD(面向对象设计).OOP(面向对象编程)三个部分有机组成 目前业界统一采用UML(统一建 ...

  3. dubbo配置文件

    <dubbo:service/> 服务配置,用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心.<dubbo:reference/> ...

  4. ueditor实现ctrl+v粘贴word图片并上传

    图片的复制无非有两种方法,一种是图片直接上传到服务器,另外一种转换成二进制流的base64码 目前限chrome浏览器使用,但是项目要求需要支持所有的浏览器,包括Windows和macOS系统.没有办 ...

  5. Noip2011 提高组 选择客栈

    P1311 选择客栈 直通 思路: ①看题,我们可以发现一个显然的性质,即当最左边的客栈向右移动时,最右边的客栈时单调向右的,并且右端点往右的客栈也符合要求.(因为只要左侧有一个满足的,右边的自然可以 ...

  6. 解决Virtualbox的根分区容量不够用问题

    现在Virtualbox新建一块磁盘.容量一定要比原来的大.然后执行克隆命令. 把原来的磁盘内容克隆到新磁盘上.然后重新启动电脑. 运行相关扩容命令即可. #克隆磁盘 cd C:\Program Fi ...

  7. 随手记录---jq如何判断当前元素是第几个元素

    主要自己总是不记得 结构如下,涉及jq中获取当前元素是父元素的的第几个元素,jq中获取某类在同类元素中占第几,each方法 <div class="parent"> & ...

  8. Python经典练习题1:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?

    Python经典练习题 网上能够搜得到的答案为: for i in range(1,85): if 168 % i == 0: j = 168 / i; if i > j and (i + j) ...

  9. pwn学习日记Day14 《程序员的自我修养》读书笔记

    目标文件:计算机科学中存放目标代码的计算机文件,包含着机器代码,代码在运行时使用的数据,调试信息等,是从源代码文件产生程序文件这一过程的中间产物. 目标代码(objectcode)指计算机科学中编译器 ...

  10. tomcat+myeclipse+mysql环境搭建

    毕业设计要做的题目,搭环境我就搭了两天...网上的教程都好碎啊.. 我的需求是这样,我有一个Android app需要与电脑上的数据库交换信息,实现增删查改,这样不管用什么方法,电脑上都要有一个服务器 ...