Reputation: 6759
I'm building a small plugin, using jQuery, but I want it to expose some of its data as public properties. For example:
$(function () {
$("#example1").myPlugin({
exampleData: 'bar'
});
$("#example2").myPlugin({
exampleData: 'foo'
});
$("#example3").myPlugin({
exampleData: 'too'
});
$('.click').click(function () {
console.log($("#example1").myPlugin('getData'));
console.log($("#example2").myPlugin('getData'));
console.log($("#example3").myPlugin('getData'));
});
});
I want the result on the console to be:
'bar'
'foo'
'too'
I tried to accomplish this with the following code:
(function ($) {
$.myPlugin = function (options) {
$.myPlugin.settings = $.extend({
exampleData: 'default value'
}, options);
}
$.fn.myPlugin = function (methodOrOptions) {
var methods = {
getData: function () {
return $(this).settings.exampleData;
},
init: function (options) {
new $.myPlugin(options);
}
}
if (methods[methodOrOptions]) {
return methods[methodOrOptions].apply($(this), Array.prototype.slice.call(arguments, 1));
} else if (typeof methodOrOptions === 'object' || !methodOrOptions) {
return methods.init.apply($(this), arguments);
} else {
$.error('Method ' + methodOrOptions + ' does not exist on jQuery.myPlugin');
}
};
})(jQuery);
But I get "Unable to get property 'exampleData' of undefined or null reference"
Can anybody help me with this?
Upvotes: 4
Views: 1499
Reputation: 1074385
The main problem is with this line:
return $(this).settings.exampleData;
$(this)
returns a jQuery set, and jQuery sets don't have a settings
property.
One main thing to remember when writing a plugin is that you get called with a jQuery set, but everything you do has to work on just subsets of that set. Example:
// Initialize on a set that includes *all* paragraphs
$("p").myPlugin();
// ...
// But now we do something with just the third paragraph; the
// plugin should expect that and store information on a per-
// element basis, not a per-set basis
$("p").eq(2).myPlugin("doSomething");
One fairly simple way to do that is to store information using jQuery's data
function.
For what it's worth, here's an example of a basic plugin that has "setData" and "getData" methods. Details in comments in the code:
(function($) {
"use strict";
// Defaults
var defaults = {
// ...
};
// Methods
var methods = {
// (Note that `initialize` isn't on this list)
getData: getData,
setData: setData
};
// Utils
var slice = Array.prototype.slice;
// Expose the plugin
$.fn.myPlugin = myPlugin;
// Main entry point to plugin
function myPlugin(arg) {
var args = slice.call(arguments, 0);
var method;
var rv;
// What are we doing?
switch (typeof arg) {
case "undefined":
case "object":
// Initializing
rv = initialize.call(this, args);
break;
case "string":
// Method, do we know it?
method = methods[arg];
if (!method) {
throw new Error("myPlugin: Unknown method '" + arg + "'");
}
args.shift(); // We've consumed the method name
// Do it, return whatever it returns
rv = method.call(this, args);
break;
default:
throw new Error("myPlugin: Expected string or object as first argument if argument given.");
}
return rv;
}
// Initialize the plugin
function initialize(args) {
// Get the options
var options = $.extend({}, defaults, args[0]);
// Loop through, initializing the elements
this.each(function() {
// ...
// (if appropriate here, you might detect whether you're being re-initialized
// for the same element)
});
// Enable chaining
return this;
}
// Get data
function getData(args) {
// "Get" operations only apply to the first element
// Return the data; normally `args` wouldn't be used
return this.first().data("myPlugin");
}
// Set data; "set" operations apply to all elements
function setData(args) {
this.data("myPlugin", args[0]);
return this;
}
})(jQuery);
Upvotes: 4