`
yuxingfirst
  • 浏览: 50592 次
  • 性别: Icon_minigender_1
  • 来自: 湘潭
社区版块
存档分类
最新评论

Plugins/Authoring

    博客分类:
  • web
阅读更多

So you've become comfortable with jQuery and would like to learn how to write your own plugins. Great! You're in the right spot. Extending jQuery with plugins and methods is very powerful and can save you and your peers a lot of development time by abstracting your most clever functions into plugins. This post will outline the basics, best practices, and common pitfalls to watch out for as you begin writing your plugin.

CONTENTS

Getting Started

To write a jQuery plugin, start by adding a new function property to the jQuery.fn object where the name of the property is the name of your plugin:

jQuery.fn.myPlugin = function() {

  // Do your awesome plugin stuff here

};

But wait! Where's my awesome dollar sign that I know and love? It's still there, however to make sure that your plugin doesn't collide with other libraries that might use the dollar sign, it's a best practice to pass jQuery to a self executing function (closure) that maps it to the dollar sign so it can't be overwritten by another library in the scope of its execution.

(function( $ ){
  $.fn.myPlugin = function() {
  
    // Do your awesome plugin stuff here

  };
})( jQuery );

Ah, that's better. Now within that closure, we can use the dollar sign in place of jQuery as much as we like.

Context

Now that we have our shell we can start writing our actual plugin code. But before we do that, I'd like to say a word about context. In the immediate scope of the plugin function, the this keyword refers to the jQuery object the plugin was invoked on. This is a common slip up due to the fact that in other instances where jQuery accepts a callback, the this keyword refers to the native DOM element. This often leads to developers unnecessarily wrapping the this keyword (again) in the jQuery function.

(function( $ ){

  $.fn.myPlugin = function() {
  
    // there's no need to do $(this) because
    // "this" is already a jquery object

    // $(this) would be the same as $($('#element'));
        
    this.fadeIn('normal', function(){

      // the this keyword is a DOM element

    });

  };
})( jQuery );
$('#element').myPlugin();

The Basics

Now that we understand the context of jQuery plugins, let's write a plugin that actually does something.

(function( $ ){

  $.fn.maxHeight = function() {
  
    var max = 0;

    this.each(function() {
      max = Math.max( max, $(this).height() );
    });

    return max;
  };
})( jQuery );
var tallest = $('div').maxHeight(); // Returns the height of the tallest div

This is a simple plugin that leverages .height() to return the height of the tallest div in the page.

Maintaining Chainability

The previous example returns an integer value of the tallest div on the page, but often times the intent of a plugin is simply modify the collection of elements in some way, and pass them along to the next method in the chain. This is the beauty of jQuery's design and is one of the reasons jQuery is so popular. So to maintain chainability in a plugin, you must make sure your plugin returns thethis keyword.

(function( $ ){

  $.fn.lockDimensions = function( type ) {  

    return this.each(function() {

      var $this = $(this);

      if ( !type || type == 'width' ) {
        $this.width( $this.width() );
      }

      if ( !type || type == 'height' ) {
        $this.height( $this.height() );
      }

    });

  };
})( jQuery );
$('div').lockDimensions('width').css('color', 'red');

Because the plugin returns the this keyword in its immediate scope, it maintains chainability and the jQuery collection can continue to be manipulated by jQuery methods, such as .css. So if your plugin doesn't return an intrinsic value, you should always return the this keyword in the immediate scope of the plugin function. Also, as you might assume, arguments you pass in your plugin invocation get passed to the immediate scope of the plugin function. So in the previous example, the string 'width' becomes the type argument for the plugin function.

Defaults and Options

For more complex and customizable plugins that provide many options, it's a best practice to have default settings that can get extended (using $.extend) when the plugin is invoked. So instead of calling a plugin with a large number of arguments, you can call it with one argument which is an object literal of the settings you would like to override. Here's how you do it.

(function( $ ){

  $.fn.tooltip = function( options ) {  

    var settings = {
      'location'         : 'top',
      'background-color' : 'blue'
    };

    return this.each(function() {        
      // If options exist, lets merge them
      // with our default settings
      if ( options ) { 
        $.extend( settings, options );
      }

      // Tooltip plugin code here

    });

  };
})( jQuery );
$('div').tooltip({
  'location' : 'left'
});

In this example, after calling the tooltip plugin with the given options, the default location setting gets overridden to become 'left', while the background-color setting remains the default 'blue'. So the final settings object ends up looking like this:

{
  'location'         : 'left',
  'background-color' : 'blue'
}

This is a great way to offer a highly configurable plugin without requiring the developer to define all available options.

Namespacing

Properly namespacing your plugin is a very important part of plugin development. Namespacing correctly assures that your plugin will have a very low chance of being overwritten by other plugins or code living on the same page. Namespacing also makes your life easier as a plugin developer because it helps you keep better track of your methods, events and data.

Plugin Methods

Under no circumstance should a single plugin ever claim more than one namespace in the jQuery.fnobject.

(function( $ ){

  $.fn.tooltip = function( options ) { // THIS };
  $.fn.tooltipShow = function( ) { // IS   };
  $.fn.tooltipHide = function( ) { // BAD  };
  $.fn.tooltipUpdate = function( content ) { // !!!  };

})( jQuery );

This is a discouraged because it clutters up the $.fn namespace. To remedy this, you should collect all of your plugin's methods in an object literal and call them by passing the string name of the method to the plugin.

(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {
    
    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    
  
  };

})( jQuery );
$('div').tooltip(); // calls the init method
$('div').tooltip({  // calls the init method
  foo : 'bar'
});
$('div').tooltip('hide'); // calls the hide method
$('div').tooltip('update', 'This is the new tooltip content!'); // calls the update method

This type of plugin architecture allows you to encapsulate all of your methods in the plugin's parent closure, and call them by first passing the string name of the method, and then passing any additional parameters you might need for that method. This type of method encapsulation and architecture is a standard in the jQuery plugin community and it used by countless plugins, including the plugins and widgets in jQueryUI.

Events

A lesser known feature of the bind method is that is allows for namespacing of bound events. If your plugin binds an event, its a good practice to namespace it. This way, if you need to unbind it later, you can do so without interfering with other events that might have been bound to the same type of event. You can namespace your events by appending “.<namespace>” to the type of event you're binding.

(function( $ ){

  var methods = {
     init : function( options ) {

       return this.each(function(){
         $(window).bind('resize.tooltip', methods.reposition);
       });

     },
     destroy : function( ) {

       return this.each(function(){
         $(window).unbind('.tooltip');
       })

     },
     reposition : function( ) { // ... },
     show : function( ) { // ... },
     hide : function( ) { // ... },
     update : function( content ) { // ...}
  };

  $.fn.tooltip = function( method ) {
    
    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    
  
  };

})( jQuery );
$('#fun').tooltip();
// Some time later...
$('#fun').tooltip('destroy');

In this example, when the tooltip is initialized with the init method, it binds the reposition method to the resize event of the window under the namespace 'tooltip'. Later, if the developer needs to destroy the tooltip, we can unbind the events bound by the plugin by passing its namespace, in this case 'tooltip', to the unbind method. This allows us to safely unbind plugin events without accidentally unbinding events that may have been bound outside of the plugin.

Data

Often times in plugin development, you may need to maintain state or check if your plugin has already been initialized on a given element. Using jQuery's data method is a great way to keep track of variables on a per element basis. However, rather than keeping track of a bunch of separate data calls with different names, it's best to use a single object literal to house all of your variables, and access that object by a single data namespace.

(function( $ ){

  var methods = {
     init : function( options ) {

       return this.each(function(){
         
         var $this = $(this),
             data = $this.data('tooltip'),
             tooltip = $('<div />', {
               text : $this.attr('title')
             });
         
         // If the plugin hasn't been initialized yet
         if ( ! data ) {
         
           /*
             Do more setup stuff here
           */

           $(this).data('tooltip', {
               target : $this,
               tooltip : tooltip
           });

         }
       });
     },
     destroy : function( ) {

       return this.each(function(){

         var $this = $(this),
             data = $this.data('tooltip');

         // Namespacing FTW
         $(window).unbind('.tooltip');
         data.tooltip.remove();
         $this.removeData('tooltip');

       })

     },
     reposition : function( ) { // ... },
     show : function( ) { // ... },
     hide : function( ) { // ... },
     update : function( content ) { // ...}
  };

  $.fn.tooltip = function( method ) {
    
    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    
  
  };

})( jQuery );

Using data helps you keep track of variables and state across method calls from your plugin. Namespacing your data into one object literal makes it easy to access all of your plugin's properties from one central location, as well as reducing the data namespace which allows for easy removal if need be.

Summary and Best Practices

Writing jQuery plugins allows you to make the most out of the library and abstract your most clever and useful functions out into reusable code that can save you time and make your development even more efficient. Here's a brief summary of the post and what to keep in mind when developing your next jQuery plugin:

  • Always wrap your plugin in (function( $ ){ // plugin goes here })( jQuery );
  • Don't redundantly wrap the this keyword in the immediate scope of your plugin's function
  • Unless you're returning an intrinsic value from your plugin, always have your plugin's function return the this keyword to maintain chainability.
  • Rather than requiring a lengthy amount of arguments, pass your plugin settings in an object literal that can be extended over the plugin's defaults.
  • Don't clutter the jQuery.fn object with more than one namespace per plugin.
  • Always namespace your methods, events and data.
  • jQuery.fn is pronounced jQuery effin'
  • my ps:看完了jquery官方的插件编写文档,先摘起来,有空翻译一下....

分享到:
评论

相关推荐

    jQuery插件 tabBox实现代码

    查了一下jq的官方插件编写文档(http://docs.jquery.com/Plugins/Authoring)以及文档中推荐的Mike Alsup写的一篇A Plugin Development Pattern。英语不是很好,但还是努力看下来(既学习到知识又能练习英语,...

    jQuery完全实例.rar

    jQuery1.2 API 中文版折叠展开折叠全部展开全部 英文说明 核心jQuery 核心函数 jQuery(expression,[context]) jQuery(expression,[context]) 这个函数接收一个包含 CSS 选择器的字符串,然后用这个字符串去匹配一组...

    Pro Grunt.js(Apress,2015)

    Author James Cryer takes you from initial installation all the way through to authoring successful plugins. Using hands-on examples you will learn about CSS linting, combination, compilation and ...

    Mastering PostCSS for Web Design(PACKT,2016)

    Get familiar with using plugins, and extend PostCSS with the API Build a fully working custom preprocessor and test it on different sites such as WordPress Write a custom syntax in PostCSS while still...

    jqurey 1.6 api 最新版中文手册

    Ajax 属性(Attributes) 核心(Core) CSS 数据(Data) 延迟对象(Deferred object) ...插件编写(Plugin Authoring) Plugins 属性(Properties) 选择器(Selectors) 遍历(Traversing) 工具(Utilities)

    DOTS Runtime & Project Tiny_ Getting Started.pdf

    详细介绍了Tiny项目的内部结构,包括场景和子场景(Scenes and Subscenes)、游戏对象和行为(GameObject and Behavior Authoring)、"RootAssembly",以及***子集等。 8. 构建配置: 讲解了如何设置不同类型的构建...

    eac3to V3.17

    v3.17 * updated libFLAC to version 1.2.1 * added a flush after every log line to help GUIs * "eac3to some.mpls" now also works if the stream files aren't there, anymore * fixed: number of subtitles ...

    swf反编译工具

    它可能提供了一个图形用户界面,让用户可以加载SWF文件,查看其内部结构,并可能支持导出为FLA(Flash Authoring File)或其他可编辑格式。 3. **SwfModify.exe.manifest**:这是一个应用程序清单文件,用于指定...

    Webpack2 中文文档 pdf

    - **Authoring Libraries**:编写库时,需要考虑如何让使用者能够方便地使用这些库。 - **改善构建性能**:Webpack 提供了一系列策略来加快构建速度,包括使用更高效的 loader、并行构建等。 #### 处理兼容性问题 ...

Global site tag (gtag.js) - Google Analytics