I need to write a jQuery plugin that doesn't take an element to work.
Example call:
$.funkyTown();
... not called like this:
$('#foo').funkyTown();
...in other words, I need this plugin to act more like a "utility" plugin (vs. apply itself directly to a matched element(s)).
Here's what I have written so far:
;(function($, window, document, undefined) {
var console = this.console || { log : $.noop, warn: $.noop }, // http://api.jquery.com/jQuery.noop/
defaults = {
foo : 'bar',
// Callbacks:
onInit : $.noop, // After plugin data initialized.
onAfterInit : $.noop // After plugin initialization.
// Using $.noop shorter than function() {} and slightly better for memory.
},
settings = {},
methods = {
// Initialize!
// Example call:
// $.funkyTown({ foo : 'baz' });
// @constructor
init : function(options) {
settings = $.extend({}, defaults, options);
settings.onInit.call(this, 'that');
console.log('1. init:', settings.foo, _foo_private_method(), methods.foo_public_method());
console.warn('2. I\'m a warning!');
settings.onAfterInit.call(this);
return this; // Is this needed for chaining?
},
// Example call:
// console.log($.funkyTown('foo_public_method', 'Wha?'));
foo_public_method : function(arg1) {
arg1 = (typeof arg1 !== 'undefined') ? arg1 : 'Boo!';
return 'foo_public_method(), arg1: ' + arg1;
},
// Might need to give users the option to destroy what this plugin created:
destroy : function() {
// Undo things here.
}
},
// The _ (underscore) is a naming convention for private members.
_foo_private_method = function() {
return '_foo_private_method(), settings.foo: ' + settings.foo;
};
// Method calling logic/boilerplate:
$.funkyTown = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if ((typeof method === 'object') || ( ! method)) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.funkyTown.');
}
};
}(jQuery, window, document));
The above is called like so:
$(document).ready(function() {
// Calls init and modifies default options:
$.funkyTown({ foo : 'baz' });
// Access to public method:
console.log($.funkyTown('foo_public_method', 'Wha?'));
});
My questions:
- Do you see anything out of the ordinary with my above plugin template? If so, how could it be improved?
- Related to #1 above: As you can probably see, I'm trying to account for the various needs like passing
options
,private
andpublic
methods andconsole
handling... What am I missing in terms of useful features? This lineUpdate: Looks like I just needed to use a different tool.$.funkyTown = function(method) {
makes the javascript linter throw a warning: "warning: anonymous function does not always return a value"; is there anyway for me to fix this? Could I just addreturn false
to the end (right before the closing};
?- Because I'm writing a utility plugin (one that will never be used directly on an element) do I still need to
return this
in my public methods in order to make things chainable (see thereturn this; // Is this needed for chaining?
line of code above)? Should I even worry about chaining for this type of plugin? - Could you provide any other feedback to help me improve my code?
What's the easiest/best way to passUpdate: Doi! This was an easy one! I simply needed to initialize mysettings
frominit
to otherprivate
andpublic
functions? Normally, I'd use.data()
on$(this)
to storesettings
and other stateful vars... Because there's not element, should I just passsettings
as an argument to the other methods?settings
outside of my public methods object.
UPDATE 1:
I've updated my code (above) to reflect the things I've learned (i.e. the strike-through lines in numeric list above) since posting this question.
I've also added a new feature:
;(function($, window, document, undefined) {
// ...
}(jQuery, window, document));
I found that I needed access to window
a few times already in my real script... After some Googling, I found this awesome resource:
JavaScript Patterns Collection
... which led me to here:
Lightweight - perfect as a generic template for beginners and above
... specifically:
// the semi-colon before the function invocation is a safety
// net against concatenated scripts and/or other plugins
// that are not closed properly.
;(function ( $, window, document, undefined ) {
// undefined is used here as the undefined global
// variable in ECMAScript 3 and is mutable (i.e. it can
// be changed by someone else). undefined isn't really
// being passed in so we can ensure that its value is
// truly undefined. In ES5, undefined can no longer be
// modified.
// window and document are passed through as local
// variables rather than as globals, because this (slightly)
// quickens the resolution process and can be more
// efficiently minified (especially when both are
// regularly referenced in your plugin).
})( jQuery, window, document );
Update 2:
I've added callbacks. I've also decided to use $.noop
in place of function() {}
:
... typing $.noop is 6 chars shorter than function(){}. Also if you use this everywhere instead of creating new, anonymous, empty functions, you'll slightly cut down on memory. – Marco
Now I'm wondering what my callbacks should return in a utility plugin? Sending this
doesn't seem that useful.
this
to continue chaining. Yeah it might not seem useful, but it's what someone would expect if they just walked in on the plugin. \$\endgroup\$init
method (in my plugin example above), I'm returningthis
at the end ofinit
; is this a good spot to do that? In my last update, I was asking specifically about my callback methods; I just wanted to clarify about myinit
method as well. Thank you! \$\endgroup\$