views:

254

answers:

3

I have created a jQuery plugin that works great with the exception of being able to call the plugin on different objects and each object retaining the options it was given. The problem is that if I call the plugin on one object, say:

$('#myDiv1').myPlugin({
    option1: 'some text',
    option2: true,
    option3: 'another option value'
});

then call the plugin again on another object, say:

$('#myDiv2').myPlugin({
    option1: 'different text',
    option2: false,
    option3: 'value for myDiv2'
});

Then if I go back and try to do something with #myDiv1 that needs its original options to still be intact, ie:

$('#myDiv1').myPlugin.update();

it won't have it's original options, but they will be overridden by the options for #myDiv2. What's the proper way to do this so that each object will retain the original options given to it? (And here's some example code of what I'm doing in the plugin)

(function($) {
$.fn.myPlugin = function(options) {

    // build main options before element iteration
    var opts = $.extend({}, $.fn.myPlugin.defaults, options);

    _option1 = opts.option1;
    _option2 = opts.option2;
    _option3 = opts.option3;

    // iterate all matched elements
    return this.each(function() {
        callPluginFunctions( this, opts );
    });

};

    ....code continued....

I realize this is some kind of scope creep or something. So, how do I get my options to stay attached and remain in the scope of the original object (ie #myDiv1) that they were given to.

EDIT: In doing some research I see that you can store data to an object using jQuery's .data function, and the docs say jQuery UI uses it extensively. Would the proper thing to do here be store the options on the object using .data, then when referenced later use the options stored in .data ???

A: 

Your options are plugin-wide. Move them inside the each(). (Why are you asking this question? You pretty much spell out the solution yourself.)

reinierpost
I'm asking because I obviously don't know the solution. I have moved everything that came before the this.each function into the this.each(function() and am still getting the same results. Could you please clarify what you mean?
Ryan
Now I'm really puzzled. What I mean is that you want the _option1, etc. to be separate for each matched element, so you should copy them e.g. like Pickle is doing. You haven't shown us how you access the options later, perhaps there is a further problem there.
reinierpost
Yes, that was my whole issue... accessing the options later. I was not attaching the configuration to the object itself. I added a $(this).data('opts', opts); in the each() loop, and now everything works like a charm. Thanks anyways.
Ryan
A: 

I've been having the same problem, but only functions passed were being overwritten. Straight up properties were persisting.

Anyway, to further explain what ~reinierpost meant, changing your code to this should work. You'll have to pass 'opts' everywhere, but I think that's necessary to take the options out of the plugin-wide namespace.

$.fn.myPlugin = function(options) {

    // iterate all matched elements
    return this.each(function() {
        // build main options before element iteration
        var opts = $.extend({}, $.fn.myPlugin.defaults, options);
        callPluginFunctions( this, opts );
    });
};

Edit: Here's code from my plugin

this.each(function() {
    var thisConfig = config;
    thisConfig = $.extend(thisConfig,settings);
    $.attach($(this),thisConfig);
});

...

$.attach = function($target,config){

So I have no plugin-wide "config" - just inside each function.

Pickle
Hey, I tried that after reinierpost's post, but I'm still having the same issue. The issue is not that I can't call separate ones with different options, but after I have 'initialized' the plugin on one object, then initialized a different object with different options, when I go back and call any plugin functions on the first item, it uses the settings that were given to the second item, and not the first. I hope that was clear, let me know if not.
Ryan
I've updated my initial entry to show how I used this technique in my plugin. Maybe you're referring to a plugin-wide configuration object rather than the one initialized in your this.each() loop?
Pickle
Yes, it was attaching the options to the object that I was missing. I see you used $.attached, I ended up using $.data ( $(this).data('opts', opts); ). Is there any real difference between the two? Thanks
Ryan
Pickle
+2  A: 

First, you will generally want to handle the command within your extension method. Second, you should be attaching configurations to each item...

(function($){
var defaultOptions = { /* default settings here */ };

//called on the native object directly, wrap with $(obj) if needed.
function initializeObject(obj, options) {
  //assign the options to the object instance.
  $(obj).data('myPlugin-options', $.extend(defaultOptions, options) );
//do other initialization tasks on the individual item here... }
function updateObject(obj) { // use $(obj).data('myPlugin-options'); }
$.fn.myPlugin = function(command, option, val) {
if (typeof command == object) { //initialization return this.each(function(){ initializeObject(this, command); }); }
if (typeof command == string) { // method or argument query switch (command.toLowerCase()) { case 'update': return this.each(function() { updateObject(this); });
//other commands here. } }
}
})(jQuery)

With the above example, you have a generic template for a jQuery extension, It's usually good form to have the following convention for use..

Initialization:
  $(...).myPlugion({ initialization-options-here });
Command: $(...).myPlugin('command-name'); //where command can be update, etc.
Get Option: var myValue = $(...).myPlugin('option', 'option-name');
Set Option: $(...).myPlugin('option', 'option-name', newValue);

Updated to use .data off of each individual obj.

Tracker1
Awesome, thanks. I was missing the 'attaching configurations to each object' part. I ended up using $.data to attach the config instead of your suggestion. Are there any pros/cons to either method? I figure they are probably about the same. Also, I like your plugin setup a lot, very similar to jQuery UI. I have adopted that as well =) Thanks!!
Ryan