views:

84

answers:

2

I've written basic jQuery plugins before, but I'm struggling to get my head around something more complex. I'm looking to emulate the API of jQuery UI, which works like this:

$('#mydiv').sortable({name: 'value'}); // constructor, options
$('#mydiv').sortable("serialize"); // call a method, with existing options
$('mydiv').sortable('option', 'axis', 'x'); // get an existing option

I've tried the following:

(function($){
    $.fn.myPlugin = function(cmd){
        var config = {
            default: 'defaultVal'
        };

        if(typeof cmd === 'object'){
            $.extend(config, cmd);
        }

        function _foo(){
            console.log(config.default);
        }

        if(cmd==='foo'){
            return _foo();
        }

        this.each(function(){
            // do default stuff
        });
    }
})(jQuery);

$('#myElement').myPlugin({default: 'newVal'});
$('#myElement').myPlugin('foo');

What I would like to see here is 'newval' being logged, but I'm seeing 'defaultVal' instead; the plugin is being called and started from scratch every time I call .myPlugin() on the element.

I've also tried using _foo.call(this) and some other variants. No joy.

In a way, I understand why this is happening, but I know that it must be possible to do it the same way as jQuery UI. I just can't see how!

(I appreciate that jQuery UI uses the widget factory to handle all of this, but I don't want to make that a requirement for the plugin.)

+3  A: 

Perhaps what you want is this...

(function($){

    var config = {
        default: 'defaultVal'
    };

    $.fn.myPlugin = function(cmd){

        if(typeof cmd === 'object'){
            $.extend(config, cmd);
        }

        function _foo(){
            console.log(config.default);
        }

        if(cmd==='foo'){
            return _foo();
        }

        this.each(function(){
            // do default stuff
        });
    }
})(jQuery);

$('#myElement').myPlugin({default: 'newVal'});
$('#myElement').myPlugin('foo');

Move the config variable outside the myPlugin function. This change will cause config to be initialized only once: when your plugin function is created.

Drew Wills
Gah! you posted before I finished typing.
Carter Galle
Thanks, this seems to do the trick! Simple solution, and it seems obvious now. I knew it was staring me in the face. :)
John McCollum
+1  A: 

You're declaring config during the function call rather than as a closure used by it. Try this:

(function($){
    var config = {
        default: 'defaultVal'
    };
    $.fn.myPlugin = function(cmd){

        if(typeof cmd === 'object'){
            $.extend(config, cmd);
        }

        function _foo(){
            console.log(config.default);
        }

        if(cmd==='foo'){
            return _foo();
        }

        this.each(function(){
            // do default stuff
        });
    }
})(jQuery);

$('#myElement').myPlugin({default: 'newVal'});
$('#myElement').myPlugin('foo');

In addition, you could look into the jQuery data API for caching data, especially if you aren't going to have just one instance per page.

Carter Galle
Just wanted to say thanks for your answer. Much obliged.
John McCollum
Certainly. BTW, you might wish to move the other functions outside the "function", too. Look at the way jQuery defines its functions: **{ _foo: function(p1,p2){ .... }, _bar: function(p1){ ... }}**
Carter Galle
Yes, I'll do this too, keep them organised. Thanks again.
John McCollum