Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing jQuery Plugins Using Classes and Prototypes

Is it good or bad practise writing plugins this way(using class and prototypes), what are disadvatages of this code?

function PluginName(jqueryObject, options) {

}
PluginName.prototype = {
    publicMethod:function() {
    },
    _privateMethod:function() {
    }
}

// Initializing 
var myPluginInstance = new PluginName($(".mySelector"), {myOption:1});
myPluginInstance.publicMethod();
like image 555
Marvin3 Avatar asked Aug 22 '11 14:08

Marvin3


2 Answers

See the jQuery docs on plugin authoring for best practices:

  • 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.

Example

(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 );

Usage:

$('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

Defaults and Options Example

(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 );

Usage:

$('div').tooltip({
  'location' : 'left'
});
like image 80
Spycho Avatar answered Nov 12 '22 14:11

Spycho


First, as Spycho said, always wrap your plugin in

(function( $ ){
    $.fn.PluginName = function() {
        // plugin goes here
    };
})( jQuery );

to avoid conflict with other libraries that use the dollar sign.

Second, if you extend the jQuery.fn object the selection called with something like $("#myDiv") is passed to the plugin as this. This way you don't have to pass the selection as a parameter to the plugin as you've done.

Third, this you did correctly, they suggest that you pass options to the plugin as an object rather than individual parameters, this is so you can easily have and override defaults:

(function( $ ){
    $.fn.PluginName = function(options) {
        var settings = { myOption: 1 };
        if (options) {
            $.extend( settings, options );
        }
        // plugin goes here
    };
})( jQuery );

Fourth, the way you've created your _privateMethod doesn't actually make it private, to do so you could follow the pattern jQuery suggests in the plugin authoring guidelines

(function( $ ){
    var methods = {
        publicMethod: function(options) {
           var settings = { myOption: 1 };
            if (options) {
                $.extend( settings, options );
            }
        },
        _privateMethod: function() {}            
    }
    $.fn.PluginName = function(methodName, options) {
        // use some logic to control what methods are public
        if (methodName == "publicMethod") {
            return publicMethod.apply(this, Array.prototype.slice.call( arguments, 1 ));
        }
    };
})( jQuery );

This uses apply and call which are fancy built-in methods of functions for setting the scope of function calls, see the MDN reference to understand what is going on there. This way you actually have control over which methods are public versus private.

EDIT: Finally, if you want to completely maintain jQuery's fluid interface and have your plugin both accept the selection passed by $() and pass it on, in other words, be chainable, your methods need to return the collection of objects they were given:

(function( $ ){
    var methods = {
        publicMethod: function(options) {
           var settings = { myOption: 1 };
            if (options) {
                $.extend( settings, options );
            }
            return this.each(function() {
                this.value = (this.value * 1) + settings.myOption;
            });
        },
        _privateMethod: function() {}            
    }
    $.fn.PluginName = function(methodName, options) {
        // use some logic to control what methods are public
        if (methodName == "publicMethod") {
            return methods.publicMethod.apply(this, Array.prototype.slice.call( arguments, 1 ));
        }
    };
})( jQuery );

See this jsFiddle for the final working example.

like image 33
nwellcome Avatar answered Nov 12 '22 14:11

nwellcome