5

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?

5
  • $('.button').click(function(){ $('.element').pwstabs({"effect":"scaleout"}); }); ? Commented Feb 8, 2015 at 16:37
  • @guest271314 that could work in theory, but it would re-initialize the plugin, which is not something he wants :) Commented Feb 8, 2015 at 16:46
  • @guest271314, already tried, it does re-initialize the plugin Commented Feb 8, 2015 at 16:54
  • Add flag variable to init , if plugin already initiated , accept options changes , without calling init again ? Add .data("init", true) to element , if plugin already initiated at element , accept options changes without calling init again ? Commented Feb 8, 2015 at 17:05
  • That could work, and i also thought about that approach. However, the OP was asking about a specific invocation pattern so i implemented it in my answer. Commented Feb 8, 2015 at 17:15

2 Answers 2

8

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

Sign up to request clarification or add additional context in comments.

6 Comments

This is just great, thank you for a good explanation and help! :) BTW how do i handle this thing in this 'options' method? ^^
@TheZenCoder Posted a alternative approach template , specific to invocation pattern of applying string or object arguments ; though did not address internal init function . Your piece is probably what OP is looking for ; possesses far greater detail and addresses init function . Cheers:)
@guest271314 your implementation works as well. I improved my answer to support any number of arguments etc. as in your answer ;)
@AlexChizhov please check out the updates. You should be happy ;) and accept the answer if you are!
@TheZenCoder this is wonderful! Thank you very much ;) With your help next version of plugin will be much more functional :)
|
5

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.