views:

771

answers:

3

I'm trying to write a plugin that will add a few methods to a jQuery wrapper object. Basically, I want to initialize it like this:

var smart = $('img:first').smartImage();

The 'smartImage' plugin would attach 2 methods to the object referenced by 'smart', so I'll be able to do something like:

smart.saveState();
// do work
smart.loadState();

Unfortunately, I can't figure out how to attach these 2 methods to the wrapper object. The code I have follows the typical jQuery plugin pattern:

(function($)
{
    $.fn.smartImage = function()
    {
        return this.each(function()
        {
            $(this).saveState = function() { /* save */ }
            $(this).loadState = function() { /* load */ }
        }
    }
 }

After I call smartImage(), neither 'saveState' nor 'loadState' is defined. What am I doing wrong?

+4  A: 

You are actually returning a jQuery object in the smartImage() function. That is the correct way to write a jQuery plugin, so that you can chain that with other jQuery functions.

I'm not sure if there is a way to do what you want. I would recommend the following:

(function($) {
     var smartImageSave = function() { return this.each(function() { } ); };
     var smartImageLoad = function() { return this.each(function() { } ); };

     $.fn.extend( { "smartImageSave":smartImageSave, 
                     "smartImageLoad":smartImageLoad });
 });

 var $smart = $("img:first");
 $smart.smartImageSave();
 $smart.smartImageLoad();

That's one technique that I am familiar with and have used successfully.

jonstjohn
+2  A: 

I'm not entirely sure why you are designing your plugin this way - you might want to consider something along the lines of jonstjohn's suggestion - but the following should be similar to what you're asking for:

jQuery.fn.extend({
    smartImage: function() {
        return {
            saveState: function() { /* save */ },
            loadState: function() { /* load */ }
        };
    }
});
Alex Barrett
+3  A: 

You cannot attach methods/properties in they way you have demonstrated, because as you said, the jQuery object is just a wrapper around whatever DOM elements it contains. You can certainly attach methods/properties to it, but they will no persist when they are selected by jQuery again.

var a = $('.my-object');
a.do_something = function(){ alert('hiya'); }
a.do_something(); // This alerts.

var b = $('.my-object');
b.do_something(); // Freak-out.

If you want a method to exist on the jQuery object when recovering it a second time, it needs to be assigned to jQuery.fn. So you could define your secondary method, assign it to the jQuery.fn, and use the jQuery data setup to maintain state for you...

$.fn.setup_something = function()
{
    this.data('my-plugin-setup-check', true);
    return this;
}

$.fn.do_something = function()
{
    if (this.data('my-plugin-setup-check'))
    {
     alert('Tada!');
    }
    return this;
}

var a = $('.my-object').setup_something();
a.do_something(); // Alerts.
$('.my-object').do_something(); // Also alerts.

I suggest you look at http://docs.jquery.com/Internals/jQuery.data and http://docs.jquery.com/Core/data#name

Mike Boers
I didn't know about this 'data' map thing, which turns out to be exactly what I wanted. Thanks!
Outlaw Programmer