Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery plugin - update settings after initialization

I have a jQuery plugin, and I want to be able to change options on the fly, like this example: $('.element').pwstabs('options','effect',scale) or something simular to it. I tried adding update: function, tried adding Plugin.prototype.update, but still cant figure out how to do that :)

Here's the structure of the plugin:

    ;(function ($, window, document, undefined) {

  var pluginName = "pwstabs",
    defaults = {
      effect: 'scaleout',
      defaultTab: 1,                
      containerWidth: '100%',       
      tabsPosition: 'horizontal',   
      horizontalPosition: 'top',    
      verticalPosition: 'left',     
      responsive: false,            
      theme: '',                    
      rtl: false,                   
      controlls: false,
      next: '',
      prev: '',
      first: '',
      last: '',
      auto: false,
      play: '',
      pause: ''
    };


  function Plugin(element, options) {
    this.element = $(element);
    this.$elem = $(this.element);
    this.settings = $.extend({}, defaults, options);
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
  }

  Plugin.prototype = {

    init: function(){

      // Here's the code for the plugin

    }

  };

  $.fn[pluginName] = function ( options ) {
    return this.each(function () {
      new Plugin( this, options );
    });
  };

})(jQuery, window, document);

So now I use the plugin like:

$('.element').pwstabs({
  effect: 'scalein',
  defaultTab: 2
});

And when I click a button, i want to change effect to lets say scaleout. With code like:

$('.button').click(function(){
  $('.element').pwstabs('options','effect','scalein');
});

So how do I implement this in the plugin?

like image 835
Alex Chizhov Avatar asked Feb 08 '15 15:02

Alex Chizhov


2 Answers

Currently the only supported invocation pattern in that plugin is to send in an object literal containing the settings to overwrite the defaults. E.g.:

$('.element').pwstabs({
  effect: 'scalein',
  defaultTab: 2
});

That invocation pattern is defined in the following method:

$.fn[pluginName] = function ( options ) {
    return this.each(function () {
        new Plugin( this, options );
    });
};

As you see, a dictionary of options is sent as the only parameter to the constructor function Plugin() to build the plugin and initialize it.

To support the invocation pattern you need, you would have to modify this method to support both invocation patterns (initialization with an object literal, but also invoking any method with more params, like your options setting method).

Here is an improved function that will handle both invocation patterns. In addition it will also store the instance of a plugin on an element, so you can access the existing settings etc. on subsequent invocations (e.g. settings changes) on the same element.

$.fn[pluginName] = function (options) {

    // get the arguments 
    var args = $.makeArray(arguments),
        after = args.slice(1);

    return this.each(function () {

        // check if there is an existing instance related to element
        var instance = $.data(this, pluginName);

        if (instance) {
            if (instance[options]) {
                instance[options].apply(instance, after);
            } else {
                $.error('Method ' + options + ' does not exist on Plugin');
            }
        } else {
            // create the plugin
            var plugin = new Plugin(this, options);

            // Store the plugin instance on the element
            $.data(this, pluginName, plugin);
            return plugin;
        }
    });
}

This would allow you to invoke the plugin as requested:

$('.element').pwstabs('options','effect','slidedown');

However, this implies you have an 'options' method in the Plugin prototype, so make sure to add one:

Plugin.prototype = {

    options: function (option, val) {
        this.settings[option] = val;
    },

    // Constructing Tabs Plugin
    init: function () {
        // omitted code for brevity
    }
}

As you see the options settings just sets the new option on the existing instance. Very simple and efficient. The new setting will be picked up by the click method handler and voila!

Here is a jsFiddle with example code in case you have trouble implementing what i was describing so far:

http://jsfiddle.net/7whs3u1n/6/

Update: I have much improved my answer to get rid of unneeded stuff, include more details and a full implementation that works (check the fiddle above) ;) i hope that this answers your question!

Adding statefulness to your plugin wasn't hard, but when you have spare time also check the alternative mechanism for writing stateful jQuery stateful plugins called jQuery widget factory:

http://learn.jquery.com/plugins/stateful-plugins-with-widget-factory/

In the future you can consider rewriting your plugin to use the widget factory. It would certainly make your code simpler ;)

like image 113
Faris Zacina Avatar answered Oct 22 '22 10:10

Faris Zacina


Try this pattern

(function ($) {
    var defaults = {
        "text": "abcdefg",
    }
    , options = $.extend({}, defaults, options);
    $.fn.plugin = function (options) {
        var options = (function (opts, def) {
            var _opts = {};
            if (typeof opts[0] !== "object") {
                _opts[opts[0]] = opts[1];
            };
            return opts.length === 0 
                   ? def 
                   : typeof opts[0] === "object" 
                     ? opts[0] : _opts
        }([].slice.call(arguments), defaults));
        return $(this).text(options.text)
    }
}(jQuery));

$(".results:eq(0)").plugin(); // return `defaults`
$(".results:eq(1)").plugin({"text":"gfedcba"}); // return `options`
$(".results:eq(2)").plugin("text", 123); // return `arguments` as `options`

    (function ($) {
        var defaults = {
            "text": "abcdefg",
        }
        , options = $.extend({}, defaults, options);
        $.fn.plugin = function (options) {
            var options = (function (opts, def) {
                var _opts = {};
                if (typeof opts[0] !== "object") {
                    _opts[opts[0]] = opts[1];
                };
                return opts.length === 0 
                       ? def 
                       : typeof opts[0] === "object" 
                         ? opts[0] : _opts
            }([].slice.call(arguments), defaults));
            return $(this).text(options.text)
        }
    }(jQuery));
    
    $(".results:eq(0)").plugin(); // return `defaults`
    $(".results:eq(1)").plugin({"text":"gfedcba"}); // return `options`
    $(".results:eq(2)").plugin("text", 123); // return `arguments` as `options`
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="results"></div><br />
<div class="results"></div><br />
<div class="results"></div>
like image 29
guest271314 Avatar answered Oct 22 '22 09:10

guest271314