jQuery UI Widget 原理

先看下代码的相关注释:

Javascript代码

  1 /*!
  2  * jQuery UI Widget 1.8.1
  3  *
  4  * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  5  * Dual licensed under the MIT (MIT-LICENSE.txt)
  6  * and GPL (GPL-LICENSE.txt) licenses.
  7  *
  8  * http://docs.jquery.com/UI/Widget
  9  */
 10 (function( $ ) {
 11
 12 var _remove = $.fn.remove;
 13
 14 $.fn.remove = function( selector, keepData ) {
 15     return this.each(function() {
 16         if ( !keepData ) {
 17             if ( !selector || $.filter( selector, [ this ] ).length ) {
 18                 $( "*", this ).add( this ).each(function() {
 19                     $( this ).triggerHandler( "remove" );
 20                 });
 21             }
 22         }
 23         //dom元素在被删除前,触发一下remove事件,jquery框架本身没有对元素删除绑定事件
 24         return _remove.call( $(this), selector, keepData );
 25     });
 26 };
 27
 28 $.widget = function( name, base, prototype ) {
 29     var namespace = name.split( "." )[ 0 ],
 30         fullName;
 31     name = name.split( "." )[ 1 ];
 32     fullName = namespace + "-" + name;
 33     //比如ui.tab,上面的name=‘tab‘;fullName=‘ui-tab‘;
 34
 35     if ( !prototype ) {
 36         prototype = base;
 37         base = $.Widget;
 38     }
 39     //如果没有prototype,那么prototype就是base参数,实际base默认为$.Widget
 40
 41     // create selector for plugin
 42     $.expr[ ":" ][ fullName ] = function( elem ) {
 43         return !!$.data( elem, name );
 44     };
 45
 46     $[ namespace ] = $[ namespace ] || {};//是否有命名空间
 47     $[ namespace ][ name ] = function( options, element ) {//根据上面的例子,即初始化了$.ui.tab=func
 48         // allow instantiation without initializing for simple inheritance
 49         if ( arguments.length ) {
 50             this._createWidget( options, element );
 51         }
 52     };
 53
 54     var basePrototype = new base();//初始化,一般都是调用了new $.Widget()
 55     // we need to make the options hash a property directly on the new instance
 56     // otherwise we‘ll modify the options hash on the prototype that we‘re
 57     // inheriting from
 58 //  $.each( basePrototype, function( key, val ) {
 59 //      if ( $.isPlainObject(val) ) {
 60 //          basePrototype[ key ] = $.extend( {}, val );
 61 //      }
 62 //  });
 63     basePrototype.options = $.extend( {}, basePrototype.options );//初始化options值,注意不需要深度拷贝
 64     $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
 65         namespace: namespace,
 66         widgetName: name,
 67         widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
 68         widgetBaseClass: fullName
 69     }, prototype );
 70     //为新的ui模块创建原型,使用深度拷贝,在basePrototype上扩展一些模块基本信息,在扩展prototype,比如ui.tabs.js中就是tab的拥有各种方法的大对象
 71
 72     $.widget.bridge( name, $[ namespace ][ name ] );//将此方法挂在jQuery对象上
 73 };
 74
 75 $.widget.bridge = function( name, object ) {
 76     $.fn[ name ] = function( options ) {
 77         var isMethodCall = typeof options === "string",
 78             args = Array.prototype.slice.call( arguments, 1 ),
 79             returnValue = this;
 80         //如果第一个参数是string类型,就认为是调用模块方法
 81         //剩下的参数作为方法的参数,后面会用到
 82
 83         // allow multiple hashes to be passed on init
 84         options = !isMethodCall && args.length ?
 85             $.extend.apply( null, [ true, options ].concat(args) ) :
 86             options;
 87         //可以简单认为是$.extend(true,options,args[0],...),args可以是一个参数或是数组
 88
 89         // prevent calls to internal methods
 90         if ( isMethodCall && options.substring( 0, 1 ) === "_" ) {
 91             return returnValue;
 92         }
 93         //开头带下划线的方法都是私有方法,不让调用
 94
 95         if ( isMethodCall ) {//如果是调用函数
 96             this.each(function() {
 97                 var instance = $.data( this, name ),//得到实例,实例作为一个数据和元素关联上
 98                     methodValue = instance && $.isFunction( instance[options] ) ?
 99                         instance[ options ].apply( instance, args ) ://如果实例和方法均存在,调用方法,把args作为参数传进去
100                         instance;//否则返回undefined
101                 if ( methodValue !== instance && methodValue !== undefined ) {//如果methodValue不是jquery对象也不是undefined
102                     returnValue = methodValue;
103                     return false;//跳出each,一般获取options的值会走这个分支
104                 }
105             });
106         } else {//不是函数调用的话
107             this.each(function() {
108                 var instance = $.data( this, name );
109                 if ( instance ) {//实例存在
110                     if ( options ) {//有参数
111                         instance.option( options );//调用option函数,一般是设置状态之类的操作
112                     }
113                     instance._init();//再次调用此函数,根据options调整
114                 } else {
115                     $.data( this, name, new object( options, this ) );
116                     //没有实例的话,给元素绑定一个实例。注意这里的this是dom,object是模块类
117                 }
118             });
119         }
120
121         return returnValue;//返回,有可能是jquery对象,有可能是其他值
122     };
123 };
124
125 $.Widget = function( options, element ) {//所有模块的基类
126     // allow instantiation without initializing for simple inheritance
127     if ( arguments.length ) {//如果有参数,调用初始化函数
128         this._createWidget( options, element );
129     }
130 };
131
132 $.Widget.prototype = {
133     widgetName: "widget",
134     widgetEventPrefix: "",
135     options: {
136         disabled: false
137     },//上面的属性会在创建模块时被覆盖
138     _createWidget: function( options, element ) {
139         // $.widget.bridge stores the plugin instance, but we do it anyway
140         // so that it‘s stored even before the _create function runs
141         this.element = $( element ).data( this.widgetName, this );//缓存实例,保存jquery对象
142         this.options = $.extend( true, {},
143             this.options,
144             $.metadata && $.metadata.get( element )[ this.widgetName ],
145             options );//参数处理
146
147         var self = this;
148         this.element.bind( "remove." + this.widgetName, function() {
149             self.destroy();
150         });//注册销毁事件
151
152         this._create();//创建
153         this._init();//初始化
154     },
155     _create: function() {},
156     _init: function() {},
157
158     destroy: function() {//销毁模块:去除绑定事件、去除数据、去除样式、属性
159         this.element
160             .unbind( "." + this.widgetName )
161             .removeData( this.widgetName );
162         this.widget()
163             .unbind( "." + this.widgetName )
164             .removeAttr( "aria-disabled" )
165             .removeClass(
166                 this.widgetBaseClass + "-disabled " +
167                 "ui-state-disabled" );
168     },
169
170     widget: function() {//返回jquery对象
171         return this.element;
172     },
173
174     option: function( key, value ) {//设置选项函数
175         var options = key,
176             self = this;
177
178         if ( arguments.length === 0 ) {
179             // don‘t return a reference to the internal hash
180             return $.extend( {}, self.options );//返回一个新的对象,不是内部数据的引用
181         }
182
183         if  (typeof key === "string" ) {
184             if ( value === undefined ) {
185                 return this.options[ key ];//取值
186             }
187             options = {};
188             options[ key ] = value;//设置值
189         }
190
191         $.each( options, function( key, value ) {
192             self._setOption( key, value );//调用内部的_setOption
193         });
194
195         return self;
196     },
197     _setOption: function( key, value ) {
198         this.options[ key ] = value;
199
200         if ( key === "disabled" ) {//增加或是去除className
201             this.widget()
202                 [ value ? "addClass" : "removeClass"](
203                     this.widgetBaseClass + "-disabled" + " " +
204                     "ui-state-disabled" )
205                 .attr( "aria-disabled", value );
206         }
207
208         return this;
209     },
210
211     enable: function() {
212         return this._setOption( "disabled", false );
213     },
214     disable: function() {
215         return this._setOption( "disabled", true );
216     },
217
218     _trigger: function( type, event, data ) {
219         var callback = this.options[ type ];
220
221         event = $.Event( event );
222         event.type = ( type === this.widgetEventPrefix ?
223             type :
224             this.widgetEventPrefix + type ).toLowerCase();
225         data = data || {};
226
227         // copy original event properties over to the new event
228         // this would happen if we could call $.event.fix instead of $.Event
229         // but we don‘t have a way to force an event to be fixed multiple times
230         if ( event.originalEvent ) {//把原始的event属性重新赋到event变量上
231             for ( var i = $.event.props.length, prop; i; ) {
232                 prop = $.event.props[ --i ];
233                 event[ prop ] = event.originalEvent[ prop ];
234             }
235         }
236
237         this.element.trigger( event, data );
238
239         return !( $.isFunction(callback) &&
240             callback.call( this.element[0], event, data ) === false ||
241             event.isDefaultPrevented() );
242     }
243 };
244
245 })( 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的实例。

时间: 2024-11-02 23:32:04

jQuery UI Widget 原理的相关文章

jQuery ui widget和jQuery plugin的实现原理简单比较

一.创建 1.  jQuery plugin (function($){ $.fn.MyPlugin=function(){ //js代码 } })(jQuery) 为了与页面上其他代码友好相处,将plugin定义在一个闭包里,MyPlugin是plugin的名字.调用方式:$('选择器').MyPlugin(); 2.  jquery ui widget (function($){ $.widget('ui.mywidget',{ options:{ //默认的配置参数 }, //方法的定义

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

先看下代码的相关注释: /*! * 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

jQuery UI Widget(1.8.1)工作原理

/*! * 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 = $

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

□ 实现Accordion高度一致 <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <link href="~/Content/jquery-ui.min.css" rel="stylesheet" /> <script src="~/S

jquery ui widget 源代码分析

jquery ui 的全部组件都是基于一个简单,可重用的widget. 这个widget是jquery ui的核心部分,有用它能实现一致的API.创建有状态的插件,而无需关心插件的内部转换. $.widget( name, base, prototype ) widget一共同拥有2或3个參数.base为可选. 这里之所以把base放在第二个參数里,主要是由于这样写代码更直观一些.(由于后面的prototype 是个代码很长的大对象). name:第一个參数是一个包括一个命名空间和组件名称的字符

jquery ui widget 源码分析

jquery ui 的所有组件都是基于一个简单,可重用的widget. 这个widget是jquery ui的核心部分,实用它能实现一致的API,创建有状态的插件,而无需关心插件的内部转换. $.widget( name, base, prototype ) widget一共有2或3个参数.base为可选. 这里之所以把base放在第二个参数里,主要是因为这样写代码更直观一些.(因为后面的prototype 是个代码非常长的大对象). name:第一个参数是一个包含一个命名空间和组件名称的字符串

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

使用 jQuery UI Widget Factory 编写有状态的插件(Stateful Plugins) Note 这一章节的内容是基于 Scott Gonzalez 一篇博客 Building Stateful jQuery Plugins(已获作者许可) 虽然大多数的 jQuery 插件都是无状态的(stateless),也就是说, 与插件进行交互的就限于调用插件时的那一组对象, 但是有好大一部分功能需求没办法通过这种简单的插件模式来实现. 为了填补这一空白,jQuery UI 实现一套

jQuery UI 教程

jQuery UI 教程 jQuery UI 是建立在 jQuery JavaScript库上的一组用户界面交互.特效.小部件及主题.无论您是创建高度交互的 Web 应用程序还是仅仅向窗体控件添加一个日期选择器,jQuery UI 都是一个完美的选择.jQuery UI 包含了许多维持状态的小部件(Widget),因此,它与典型的 jQuery 插件使用模式略有不同.所有的 jQuery UI 小部件(Widget)使用相同的模式,所以,只要您学会使用其中一个,您就知道如何使用其他的小部件(Wi

排序插件jquery.ui.sortable

<link href="${base}/resources/sort/jquery.ui.all.css" rel="stylesheet"     type="text/css" /> <script type="text/javascript" src="${base}/page/js/jquery.js"></script> <script type=&quo