views:

42

answers:

1

I'm new to writing jQuery plugins, and I'm confused as to how to let users use $(this) in their options. As a simple (and redundant) example, say I wanted to let a user append their selection's text to an element they supply in their options, like so:

$(function() {
   $('#stuff').appender({ 'placement' : this });
});

And the plugin code is:

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

        var opts = $.extend({}, $.fn.appender.defaults, options);

        return this.each( function() {
            var $el = $(this);
            $(opts.placement).append( $el.text() );
        });

    };

    $.fn.appender.defaults = {
        'placement' :   'body',
    }
})(jQuery);

If the user supplies 'body' or $('#wrapper') for 'placement' in the plugin options, of course it works as expected. But when using 'this' or '$(this)', it refers to the document itself rather than the element in the selection.

This is a pretty crappy example of a plugin, but the thing I would like to learn is how to allow plugin users to use $(this) or its attributes as values for options.

Any ideas?

A: 

You can't do this inside your plugin, unless you give it a default or a special string name, for example where "self" translates to this. To pass it as a reference they'll need a .each(), like this:

$('#stuff').each(function() }
  $(this).appender({ 'placement' : this });
});

For the inside the plugin option, I would say specifying nothing defaults to this, so it would look like this:

$(opts.placement || this).append($el.text());
//call looks like this:
$('#stuff').appender();

Or have a special string (which is less intuitive, I'd go with the default above), it would look something like this:

$(opts.placement === "self" ? this : ops.placement).append($el.text());
//call looks like this:
$('#stuff').appender({ placement: 'self' });
Nick Craver
Thanks for the quick reply! But an issue with the two solutions you suggested is that it doesn't seem like the user could get attributes of $(this) or use something like $(this).find('span') either. It's interesting that each() does intrinsically have 'this', and same thing with something like data(). I wonder how those methods have 'this' passed into them? Could it be something with the apply() or call() JS methods that could also be used in plugins?
salmonete
@salmonete - The difference is that `.each()` creates a closure, in which `this` refers to the current element in the loop...that's why it's the first option, if you want each element to reference something relatively, this is the easiest way to go about it. You could pass a function in that finds the element relatively, but `{ funcOption: function() { return $(this).find('span'); } }` isn't really that elegant with the options...but you could do it if you wanted. In that case you'd use `var $el = funcOption.call(this);` to execute that function and get your result in the proper context.
Nick Craver
Hmm I half understand what you're saying. Even after mulling over jQuery's init and each methods, I still don't quite get why when each() is chained to $(), it gets 'this' as an array of DOM elements, but when plugin() is chained to $(), it gets 'this' as document. I'm going to keep researching to see if I can wrap my head around it.
salmonete
I think I get it now. $(obj).each() accepts a callback function as a parameter, and $.each() is called internally. The 'this' inside $(obj).each(this) refers to the document, but $(obj).each(function(){this}) refers to a DOM element in obj because $.each() is calling $(obj).each()'s callback while looping through the obj array. So in a plugin, parameters passed in that are functions are called inside the plugin during its 'return this.each(function)', which is the same as 'return $.each(this, function)', wherein 'this' refers to the jQuery object since it was passed into the plugin. I think.
salmonete