4

I have created a plugin to convert an HTML select box into a custom drop down using DIV's.

All works well, but i'd like to make it a little better. see my jsFiddle

At the end of the plugin I have 2 methods, slideDownOptions & slideUpOptions, I would like to make these available outside of the plugin so other events can trigger the action.

Im getting a little confused how to do this and more specifically how to call the methods from both within the plugin AND from outside of the plugin.

Any help always appreciated

sygad1
  • 1,392
  • 3
  • 12
  • 19

2 Answers2

13

Think about refactoring your plugin using object oriented code. With this you can make API for your plugins like jQuery UI API. So you could access to plugin methods like:

$('select').customSelect(); // apply plugin to elements
$('select').customSelect('resetOpacity'); // call method resetOpacity();
$('select').customSelect('setOpacity', 0.5); // call method with arguments

Basic template for creating such plugins will look like following:

// plugin example
(function($){
    // custom select class
    function CustomSelect(item, options) {
        this.options = $.extend({
            foo: 'bar'
        }, options);
        this.item = $(item);
        this.init();
    }
    CustomSelect.prototype = {
        init: function() {
            this.item.css({opacity:0.5});
        },
        resetOpacity: function() {
            this.setOpacity('');
        },
        setOpacity: function(opacity) {
            this.item.css({opacity:opacity});
        }
    }

    // jQuery plugin interface
    $.fn.customSelect = function(opt) {
        // slice arguments to leave only arguments after function name
        var args = Array.prototype.slice.call(arguments, 1);
        return this.each(function() {
            var item = $(this), instance = item.data('CustomSelect');
            if(!instance) {
                // create plugin instance and save it in data
                item.data('CustomSelect', new CustomSelect(this, opt));
            } else {
                // if instance already created call method
                if(typeof opt === 'string') {
                    instance[opt].apply(instance, args);
                }
            }
        });
    }

}(jQuery));

// plugin testing
$('select').customSelect();

Working JS fiddle here: http://jsfiddle.net/XsZ3Z/

Inferpse
  • 4,135
  • 1
  • 31
  • 39
  • 1
    Love this template. Well done! While Javascript does not offer protected methods and properties itself, it is easy to implement using your template. A widespread convention is to start protected methods with an underscore '_' - and it is easily implemented to prevent calling those methods from outside the plugin: `// if instance already created call method if(typeof opt === 'string' && opt.charAt(0) !== '_') { instance[opt].apply(instance, args); }` – Liquinaut Aug 22 '14 at 07:49
  • This is great. Love this approach! – Aaron Lozier Sep 09 '15 at 16:33
4

You gonna have to refactor the code to get it working. Consider using the jQuery Boilerplate:

;(function ( $, window, undefined ) {

  var pluginName = 'convertSelect',
  document = window.document,
  defaults = {
    propertyName: "value"
  };

  function Plugin( element, options ) {
    this.element = element;
    this.options = $.extend( {}, defaults, options) ;

    this._defaults = defaults;
    this._name = pluginName;

    this.init();
  }

  Plugin.prototype = {

    // Private methods start with underscore
    _generateMarkup: function() {

      // you can access 'this' which refers to the constructor
      // so you have access the all the properties an methods
      // of the prototype, for example:
      var o = this.options

    },

    // Public methods
    slideDownOptions: function() { ... }

  }

  $.fn[ pluginName ] = function ( options ) {
    return this.each(function () {
      if (!$.data( this, 'plugin_' + pluginName ) ) {
        $.data( this, 'plugin_' + pluginName, new Plugin( this, options ) );
      }
    });
  };

}(jQuery, window));

Then you can can call public methods like so:

var $select = $('select').convertSelect().data('plugin_convertSelect');
$select.slideDownOptions();

I had a similar problem with a project of mine, I recently had to refactor the whole thing because I was polluting the jQuery namespace with too many methods. The jQuery Boilerplate works very well, it's based on the official jQuery guide but with some twists. If you wanna see this plugin pattern in action take a look at https://github.com/elclanrs/jq-idealforms/tree/master/js/src.

elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • Agree with using jQuery Boilerplate to start with, but then I adapted mine to allow public functions using this (v simple change at end of boilerplate code): https://github.com/jquery-boilerplate/jquery-boilerplate/wiki/Extending-jQuery-Boilerplate – jackocnr Sep 23 '13 at 00:20
  • One more for you. This is imho the best way to do this. – Marek Fajkus Feb 14 '14 at 16:14
  • jQuery chaining is totally broken with this approach. – Alex G Apr 21 '17 at 20:26