`
dingchao.lonton
  • 浏览: 49689 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

彻底理解jQuery UI widget factory

阅读更多

本文原文地址:http://wiki.jqueryui.com/w/page/12138135/Widget-factory


All of jQuery UI's widgets and interactions are built from a simple, reusable base - the jQuery UI Widget Factory. It provides a flexible base for building complex, stateful plugins with a consistent API.  It is designed not only for plugins that are part of jQuery UI, but for general consumption by developers who want to create object-oriented components without reinventing common infrastructure. It does not have any dependencies on the rest of jQuery UI, but most of jQuery UI is dependent on it.


What is it?

The widget factory is a simple function on the global jQuery object - jQuery.widget - that accepts 2 or 3 arguments.


jQuery.widget( "namespace.widgetname", /* optional - an existing widget prototype to inherit from */, /* An object literal to become the widget's prototype*/ {...} ); 


The first argument to the widget factory is a string containing a namespace  and the widget name , separated by a dot. The namespace is mandatory, and it refers to the location on the global jQuery object where the widget prototype will be stored. If the namespace does not exist, the widget factory will create it for you. The widget name serves as the actual name of the plugin function and prototype. For instance,  jQuery.widget( "demo.multi", {...} ) will create jQuery.demo jQuery.demo.multi , and jQuery.demo.multi.prototype .


The second (optional) argument is a widget prototype to inherit from. For instance, jQuery UI has a "mouse" plugin on which the rest of the interaction plugins are based. In order to achieve this, draggable, droppable, etc. all inherit from the mouse plugin like so: jQuery.widget( "ui.draggable", $.ui.mouse, {...} );  If you do not supply this argument, the widget will inherit directly from the "base widget,"  jQuery.Widget  (note the difference between lowercase "w"  jQuery.widget and uppercase "W"  jQuery.Widget ). 


The last argument to the widget factory is an object literal that will be transformed into the prototype for each instance of the widget. The widget factory sets up the prototype chain, connecting the widget's prototype to any widgets from which it inherits, up to the base jQuery.Widget .


Once you make the call to jQuery.widget , there will be a new method available on the jQuery prototype ( jQuery.fn ) that corresponds to the widget name - in the case of our example, jQuery.fn.multi . This .fn method serves as the interface between the DOM elements contained in a given jQuery object and instances of the widget prototype you created. A new instance of the widget is created for each element in the jQuery object.


Advantages


The basic approach described in the Plugin Authoring Guidelines  leaves a lot up to the implementor when it comes to implementing stateful, object-oriented plugins. Furthermore, it doesn't offer any conveniences to obscure away common plumbing tasks. The widget factory provides you with jQuery UI's API for allowing communication with your plugin instance, and abstracts a number of repetitive tasks.


  • Creation of a namespace (if necessary) and prototype
    • A pseudoselector is also generated from the namespace and name, for querying and filtering, e.g.,  $( ":demo-multi" )
  • Linkage between prototype and jQuery.fn

    • This is accomplished using jQuery.widget.bridge

  • Merging default options with user-supplied overrides
    • The default values are also exposed so that users can change the actual defaults

  • Plugin instance accessible via $( "#something" ).data( "pluginname" )
    • A reference to a jQuery object containing the DOM element is available as a property of the instance as this.element , so it is easy to go back and forth between the object and the element.

  • The widget's methods are accessible via string invocation through the method on the jQuery prototype - $( "#something" ).multi( "refresh" ) - or directly from the instance itself - $( "#something" ).data( "multi" ).refresh()

  • Prevention against multiple instantiations of the widget on the same element 

  • A mechanism for dispatching callbacks the plugin user can subscribe to: this._trigger( "clear" )

    • The user can subscribe via an option:

      • $( "#something" ).multi({ clear: function( event ) {} }); 

    • Or using .bind()

      • $( "#something" ).bind( "multiclear", function( event ) {} );

  • A mechanism for facilitating and responding to changes to plugin options after instantiation
    • $( "#something" ).multi( "option", "clear" , function ( event ) { alert( "I cleared the multiselect!" ); } );
  • Easily enable or disable the widget, or destroy the instance entirely and return the element to its original state

Building Your Prototype

Infrastructure


The object literal you provide to the widget factory to serve as the widget prototype can be as complicated as you need, but at a minimum, it should contain default options , and basic _create , _setOption , and destroy callbacks

 

(function( $ ) {
  $.widget( "demo.multi", {
 
    // These options will be used as defaults
    options: { 
      clear: null
    },
 
    // Set up the widget
    _create: function() {
    },
 

    // Use the _setOption method to respond to changes to options
    _setOption: function( key, value ) {
      switch( key ) {

        case "clear":
          // handle changes to clear option
          break;
      }
 
      // In jQuery UI 1.8, you have to manually invoke the _setOption method from the base widget
      $.Widget.prototype._setOption.apply( this, arguments );
      // In jQuery UI 1.9 and above, you use the _super method instead
      this._super( "_setOption", key, value );
    },
 
    // Use the destroy method to clean up any modifications your widget has made to the DOM
    destroy: function() {

      // In jQuery UI 1.8, you must invoke the destroy method from the base widget
      $.Widget.prototype.destroy.call( this );
      // In jQuery UI 1.9 and above, you would define _destroy instead of destroy and not call the base method
    }
  });
}( jQuery ) );
 

Encapsulation into Methods

 

This object will also likely need to contain methods to handle various bits of your widget-specific functionality, like building and appending new elements or handling events.  It is wise to use discrete methods to handle each chunk of functionality, instead of handling too much directly in your _create method.  This will enable you to react to eventual changes in state without duplicating code. 


For instance, in a hypothetical widget to enhance a <select multiple>, one might have to iterate over the <option> s in the select to create a corresponding <li> in a proxy <ul>.  This could be accomplished in the _create method, like so:


_create: function() {
    var self = this;
    this.list = $( "<ul>" ).insertAfter( this.element );
    this.element.hide().find( "option" ).each(function( i, el ) {
        var $el = $( el ),
            text = $( el ).text(),
            item = $( "<li class='multi-option-item'>" + text + "</li>" );
        item.appendTo( self.list ).click(function(){ 
            console.log( $el.val() );
        });
    });
}
 


Unfortunately, leaving this code in _create makes it difficult to manage the relationship between the original  <option>  elements and the list items, or deal with the problem of reflecting the state of <option>  elements that have been added to or removed from the original <select> after the widget is instantiated. Instead, we build a refresh method, responsible exclusively for dealing with this linkage, and invoke it from _create . We'll also keep the logic for handling clicks on the list items separate, and we'll use event delegation to avoid having to bind new handlers when new list items are created.

 

 

_create: function() {
    this.list = $( "<ul>" )
        .insertAfter( this.element )
        .delegate( "li.multi-option-item", "click", $.proxy( this._itemClick, this ) );
    this.element.hide();
    this.refresh();
},
refresh: function() {
    // Keep track of the generated list items

    this.items = this.items || $();
    // Use a class to avoid working on options that have already been created
    this.element.find( "option:not(.demo-multi-option)" ).each( $.proxy(function( i, el ) {
 
        // Add the class so this option will not be processed next time the list is refreshed
        var $el = $( el ).addClass( "demo-multi-option" ),
            text = $el.text(),
            // Create the list item
            item = $( "<li class='multi-option-item'>" + text + "</li>" )
                .data( "option.multi", el )
                .appendTo( this.list );
 
        // Save it into the item cache
        this.items = this.items.add( item );
 
    },this));
 
    // If the the option associated with this list item is no longer contained by the
    // real select element, remove it from the list and the cache
    this.items = this.items.filter( $.proxy(function( i, item ) {
        var isInOriginal = $.contains( this.element[0], $.data( item, "option.multi" ) );
        if ( !isInOriginal ) {
            $( item ).remove();
        }
        return isInOriginal;
    }, this ));
},
_itemClick: function( event ) {
     console.log( $( event.target ).val() );
}
 


Private vs. Public Methods

As you may have noticed, some of the methods on the prototype are prefixed with an underscore, and others are not. Methods prefixed with an underscore are considered to be "private" by jQuery UI. The widget factory will block any calls made via $.fn to a private method:  $( "#something" ).multi( "_create" ) will throw an exception. Since these private methods exist directly on the widget prototype, however, they are private by convention only. When a reference to the actual widget instance is acquired via  .data(), any of its methods can be invoked directly: $( "#something" ).data( "multi" )._create() .


So how do you know which is the appropriate decision? If your widget's users will likely have a need for a particular method's functionality, make it public. The refresh example is a case in point: Since the user will be the one manipulating elements in the original <select>, we must provide the facility for him to update the proxy. On the other hand, a plumbing function to handle input on the proxy elements the widget creates, like the _itemClick method, is only for internal use, so we make it private and block it from the widget's public API.



Properties:


this.element

The element that was used to instantiate the plugin. For example, if you were to do $( "#foo" ).myWidget() , then inside your widget instance this.element would be a jQuery object containing the element with id foo. If you select multiple elements and call .myWidget() on the collection, a separate plugin instance will be instantiated for each element. In other words, this.element will always contain exactly one element.


this.options

The options currently being used for the plugin configuration. On instantiation, any options provided by the user will automatically be merged with any default values defined in $.demo.multi.prototype.options . User specified options override the defaults.


this.namespace

The namespace the plugin lives in, in this case "demo". This is usually not needed inside of individual plugins.


this.name

The name of the plugin, in this case "multi". Slightly more useful than this.namespace , but generally not needed inside of individual plugins.


this.widgetEventPrefix

This is used to determine how to name events that are associated with any callbacks the plugin provides. For example, dialog has a close callback, and when the close callback is executed, a dialogclose event is triggered. The name of the event is the event prefix + the callback name. The widgetEventPrefix defaults to the widget name, but can be overridden if the event names should be different. For example, when a user starts dragging an element, we don't want the name of the event to be draggablestart, we want it to be dragstart, so we override the event prefix to be "drag". If the name of the callback is the same as the event prefix, then the event name will not be prefixed. This prevents an event name like dragdrag.


this.widgetBaseClass


This is useful for building class names to use on elements within your widget. For example, if you wanted to mark an element as being active, you could do element.addClass( this.widgetBaseClass + "-active" ) . This isn't really necessary to use in individual plugins because you can just as easily do .addClass( "demo-multi-active" ) . This is more for use inside the widget factory and abstract plugins like $.ui.mouse.


Methods:


_create

This is where you setup everything related to your widget, such as creating elements, binding events, etc. This gets run once, immediately after instantiation.


_init

This method is invoked anytime your widget is invoked without either 0 arguments or with a single options argument. This could be the first time it is invoked, in which case _init will get called after _create. It could also be called at any time after the widget create, in which case _init allows you to handle any re-initialization without forcing the user to perform a destroy->create cycle.

 

destroy

This destroys an instantiated plugin and does any necessary cleanup. All modifications your plugin performs must be removed on destroy. This includes removing classes, unbinding events, destroying created elements, etc. The widget factory provides a starting point, but should be extended to meet the needs of the individual plugin.


option

Used for getting and setting options after instantiation. The method signature is the same as .css() and .attr(). You can specify just a name to get a value, a name and value to set a value, or a hash to set multiple values. This method calls _setOptions internally, so this method should never need to be modified by an individual plugin.


_setOptions

An internal utility method that is used for setting options after instantiation. This method calls _setOption internally, so this method should never need to be modified by an individual plugin.


_setOption

Called when a user sets an option value via the option method. This method may need to be modified by an individual plugin so the plugin can react when certain options change. For example, when a dialog's title option changes, the text inside the title bar must be updated.

 

_setOption: function(key, value) {
    if (key === 'title') {
        this.titleElement.text(value);
    }
    $.Widget.prototype._setOption.apply(this, arguments);
}
 


By calling the base _setOption, we get the default side effect of setting the option to the new value. This should not be performed by _setOption. In some instances, it's necessary to compare the old and new values to determine the correct side effects. In those instance, you can compare this.options[key] with value as long as you delay the call to the base _setOption until the end. If you don't need to compare the old and new values, you can call the base _setOption at top of your _setOption function.


enable

Helper method that just calls option('disabled', false). Note that you'll want to handle this by having an if (key === "disabled") block in your _setOption


disable

Helper method that just calls option('disabled', true). Note that you'll want to handle this by having an  if (key === "disabled")  block in your _setOption


_trigger

This method must be used to execute all callbacks. The only required parameter is the name of the callback to execute. All callbacks also trigger events (see notes about this.widgetEventPrefix above). You may also provide an event object that represents the event that initiated the process. For example, a drag event is initiated by a mousemove event, so the original mousemove event object must be passed in to _trigger. The third parameter is a hash of data that will be passed as a parameter to the callback and event handlers. Data provided in this hash should only be information that is relevant to the specific event and is not readily available thorugh some other use of the plugin API.


Other Benefits and Use:


Plugins using the widget factory only deal with the plugin instance and never with the jQuery object the method is being called on. When a method is called on your plugin from a jQuery object, the widget factory will delegate the method call to the appropriate plugin instances. The widget factory will also handle chaining for you automatically. If the method returns the plugin instance, then the widget factory will make the call chainable and invoke the call on each plugin instance; if the method returns a value other than the plugin instance, that value will be returned from the original call on the jQuery object.

分享到:
评论

相关推荐

    jQuery UI Cookbook (pdf + ePub)

    “jQuery UI Cookbook” provides you with practical recipes featuring in-depth coverage of every widget in the framework, including how to address limitations that impact your everyday development ...

    jquery-ui-1.10.4.custom

    1. **Widget Factory**: jQuery UI 的基础是 Widget Factory,这是一个用于创建可重用组件的框架。它规范了组件的创建过程,使得组件具有统一的行为和生命周期管理。 2. **交互(Interactions)**: 包括如拖放...

    jquery ui 最新版1.10

    - **Widget Factory**: jQuery UI 的基础是Widget Factory,这是一个用于构建自定义UI组件的框架,它确保组件遵循一致的API和生命周期方法,如`_create()`, `_destroy()`, `option()`, 和 `disable()`等。...

    jQueryui所需要依赖的库文件

    4. **Widget Factory**:jQuery UI的组件都是基于Widget Factory构建的,这是一个强大的抽象层,使得创建和扩展组件变得简单。 5. **Interactions**:这些是处理用户交互的组件,如Draggable(可拖动)、Resizable...

    jquery-ui(全)

    1. **Widget Factory**: jQuery UI的核心是Widget Factory,它是一种用于创建自定义UI组件的机制。通过这个工厂,开发者可以方便地创建具有统一API和行为的UI元素。 2. **Interactions(交互)**: 包括拖放...

    前端开源库-jquery-widget-compiler.zip

    jQuery Widget Factory是jQuery UI项目的一部分,它提供了一种结构化的方法来创建可复用的UI组件。这个工厂模式允许开发者定义组件的行为、状态和选项,使得创建和维护复杂的用户界面变得更加容易。通过使用jQuery ...

    jquery-ui API jquery ui

    - **Widget Factory**:jQuery UI的核心机制,负责创建和管理Widgets。开发者可以通过$.widget()方法扩展新的组件。 - **Options**:每个Widget都有自己的配置选项,可以通过初始化时传递参数来设置。 - **...

    jqueryUI包

    这个压缩包"jqueryUI包"包含了完整的 jQuery UI 库,允许开发者轻松地为网页添加各种交互元素,提升用户体验。 在 jQuery UI 中,主要包含以下组件: 1. **主题**: jQuery UI 提供了可定制的主题,允许开发者根据...

    jquery-ui-1.8.5 UI设计必备

    同时,jQuery UI还提供了一种叫做"widget factory"的机制,让创建自定义组件变得更加简单和标准化。 总结来说,"jquery-ui-1.8.5"是Web开发者构建高效、美观UI的强大工具。通过理解和使用这个压缩包中的资源,...

    jQuery widget设计文档

    Widget工厂(widget factory)是jQuery UI提供的一种机制,它允许开发者定义自己的自定义组件,并确保这些组件符合统一的API标准。通过调用$.widget()函数,我们可以创建一个新的widget,传入组件名、基础类(通常是...

    struts2下jquery-ui的全部实例

    - **Widget Factory**:通过$.widget方法创建自定义组件,遵循一定的结构和规范。 - **主要组件**:包括Accordion(折叠面板)、Dialog(对话框)、DatePicker(日期选择器)、Slider(滑块)、Tabs(选项卡)等。...

    jquery-ui-1.12.1.custom.zip

    4. **Utilities**:包括Position(定位)、Widget Factory(组件工厂)等工具函数,方便开发者在实现自定义功能时调用。 5. **Examples**:这是最核心的部分,包含了各种组件的使用示例代码。通过这些代码,开发者...

    jquery-ui-1.9.2.custom

    1. **Widget Factory**:jQuery UI的核心是Widget Factory,它提供了一种统一的方式来创建可复用、可配置的组件,确保了代码的规范性和一致性。 2. **交互组件**:包括拖放(Draggable)、可叠放(Droppable)、可排序...

    JQuery UI 包

    2. **Widget Factory**: jQuery UI 的基础是Widget Factory,这是一个用于创建可重用组件的框架。所有的jQuery UI组件都是基于此工厂构建,确保了组件的一致性和可扩展性。 3. **特效**: 包括动画、过渡效果等,如...

    jQuery官方UI插件

    8. **性能优化**:在大型应用中,为了提高性能,可以使用“懒加载”策略,只在需要时初始化组件,或者利用 jQuery UI 的“widget factory”来减少代码体积。 9. **自定义构建**:根据项目需求,可以从 jQuery UI 的...

    jquery ui 1.10

    - **Widget Factory**:jQuery UI 的核心是 Widget Factory,它提供了一种统一的方式来创建可重用的组件,确保了代码的模块化和一致性。 - **Theming**:jQuery UI 1.10 继续支持其著名的 ThemeRoller 工具,允许...

    jqueryui入门

    同时,jQuery UI 提供了一系列工具,如 Widget Factory,帮助开发者构建可复用的 UI 组件。 总的来说,jQuery UI 是一个强大且灵活的前端框架,它极大地简化了网页交互设计。无论你是初学者还是经验丰富的开发者,...

    jquery.ui-1.0组件

    4. **Utilities(实用工具)**:除了小部件和特效,jQuery UI还提供了诸如Position、TABBING、Widget Factory等实用工具函数,帮助开发者处理位置计算、元素对齐、事件管理等问题。 **jQuery UI与Ajax框架组件的...

    jquery UI

    9. **Widget Factory**:jQuery UI 提供了一个Widget Factory,它是一个用于创建自定义组件的框架,使得开发者可以按照统一的模式构建自己的UI组件。 在使用jQuery UI时,需要注意的是,由于它包含了大量的...

    jquery-ui-1.8.5

    在jQuery UI 1.8.5中,最重要的组件之一是“Widget Factory”。这是一个核心机制,允许开发者将普通的DOM元素转换为具有状态、事件和配置选项的对象,从而创建自定义的UI组件。它使得组件化开发变得简单,便于维护和...

Global site tag (gtag.js) - Google Analytics