jquery widgets 开发

All of jQuery UI's widgets andinteractions are built from a simple, reusable base - the jQuery UI WidgetFactory. It provides a flexible/灵活/ base for building complex,stateful plugins with a consistent API. It is designed not only for plugins that are part ofjQuery UI, but for general consumption/使用、消费/ by developers who want to create object-oriented components withoutreinventing/重新写/ common infrastructure/基础结构/. It does not have any dependencies on the rest of jQuery UI, but most ofjQuery UI is dependent on it.

What is it?

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

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

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

The second (optional) argument is awidget prototype to inherit from. For instance, jQuery UI has a"mouse" plugin on which the rest of the interaction plugins arebased. In order to achieve this, draggable, droppable, etc. all inherit fromthe 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 widgetfactory is an object literal that will be transformed into the prototype foreach 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 tothe base jQuery.Widget.

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

Advantages

The basic approach described in the Plugin Authoring Guidelines leaves a lot up to the implementorwhen it comes to implementing stateful, object-oriented plugins. Furthermore,it doesn't offer any conveniences to obscure away common plumbing tasks. Thewidget factory provides you with jQuery UI's API for allowing communicationwith 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 tothe widget factory to serve as the widget prototype can be as complicated asyou need, but at a minimum, it should contain defaultoptions, and basic_create,_setOption, anddestroy 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 tochanges to options

    _setOption: function( key, value ) {

      switch( key ) {

        case "clear":

          // handle changes to clear option

          break;

      }

 

      // In jQuery UI 1.8, you have to manuallyinvoke the _setOption method from the base widget

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

      // In jQuery UI 1.9 and above, you usethe _super method instead

      this._super( "_setOption", key,value );

    },

 

    // Use the destroy method to clean up anymodifications your widget has made to the DOM

    destroy: function() {

      // In jQuery UI 1.8, you must invoke thedestroy method from the base widget

      $.Widget.prototype.destroy.call( this );

      // In jQuery UI 1.9 and above, you woulddefine _destroy instead of destroy and not call the base method

    }

  });

}( jQuery ) );

 

Encapsulation into Methods

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

For instance, in a hypotheticalwidget to enhance a<selectmultiple>,one mighthave to iterate over the<option>s in the select to create acorresponding<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 = $( "<liclass='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 betweenthe original<option>elements andthe list items, or deal with the problem of reflecting the state of<option> elements that have been added to orremoved from the original<select> after thewidget is instantiated. Instead, we build a refresh method, responsibleexclusively for dealing with this linkage, and invoke it from_create. We'll also keep the logic for handling clicks on thelist items separate, and we'll use event delegation to avoid having to bind newhandlers 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 optionsthat have already been created

    this.element.find("option:not(.demo-multi-option)" ).each( $.proxy(function( i, el ) {

        // Add the class so this option willnot be processed next time the list is refreshed

        var $el = $( el ).addClass("demo-multi-option" ),

            text = $el.text(),

            // Create the list item

            item = $( "<liclass='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 thislist item is no longer contained by the

    // real select element, remove it from thelist 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 themethods on the prototype are prefixed with an underscore, and others are not.Methods prefixed with an underscore are considered to be "private" byjQuery UI. The widget factory will block any calls made via $.fn to a private method:$( "#something" ).multi( "_create" ) will throw an exception. Sincethese private methods exist directly on the widget prototype, however, they areprivateby convention only.When a reference to the actual widgetinstance is acquired via.data(),any of its methods can be invokeddirectly:$("#something" ).data( "multi" )._create().

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

Properties:

this.element

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

this.options

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

this.namespace

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

this.name

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

this.widgetEventPrefix

This is used to determine how toname events that are associated with any callbacks the plugin provides. Forexample, 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 canbe overridden if the event names should be different. For example, when a userstarts dragging an element, we don't want the name of the event to bedraggablestart, we want it to be dragstart, so we override the event prefix tobe "drag". If the name of the callback is the same as the eventprefix, then the event name will not be prefixed. This prevents an event namelike dragdrag.

this.widgetBaseClass

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

Methods:

_create

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

_init

This method is invoked anytime yourwidget 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 calledafter _create. It could also be called at any time after the widget create, inwhich case _init allows you to handle any re-initialization without forcing theuser to perform a destroy->create cycle.

destroy

This destroys an instantiated pluginand does any necessary cleanup. All modifications your plugin performs must beremoved 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 optionsafter 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, ora hash to set multiple values. This method calls _setOptions internally, sothis method should never need to be modified by an individual plugin.

_setOptions

An internal utility method that isused for setting options after instantiation. This method calls _setOptioninternally, so this method should never need to be modified by an individualplugin.

_setOption

Called when a user sets an optionvalue via the option method. This method may need to be modified by anindividual plugin so the plugin can react when certain options change. Forexample, when a dialog's title option changes, the text inside the title barmust be updated.

_setOption: function(key,value) {

    if (key === 'title') {

        this.titleElement.text(value);

    }

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

}

By calling the base _setOption, weget the default side effect of setting the option to the new value. This shouldnot be performed by _setOption. In some instances, it's necessary to comparethe 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 tothe base _setOption until the end. If you don't need to compare the old and newvalues, you can call the base _setOption at top of your _setOption function.

enable

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

disable

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

_trigger

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

Other Benefits and Use:

Plugins using the widget factoryonly deal with the plugin instance and never with the jQuery object the methodis being called on. When a method is called on your plugin from a jQueryobject, the widget factory will delegate the method call to the appropriateplugin instances. The widget factory will also handle chaining for youautomatically. If the method returns the plugin instance, then the widgetfactory will make the call chainable and invoke the call on each plugininstance; if the method returns a value other than the plugin instance, thatvalue will be returned from the original call on the jQuery object.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值