I am trying to write a simple jQuery plugin for my needs, using a variant of the first one in this style guide.
;(function($) {
var plugin_name = 'my_plugin',
defaults = {};
function Plugin ( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options );
this._defaults = defaults;
this._name = plugin_name;
this.init();
}
Plugin.prototype = {
init: function () {
// Plugin code - attempt to debug
alert('hi');
}
}
$.fn[plugin_name] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + plugin_name)) {
$.data(this, 'plugin_' + plugin_name, new Plugin( this, options ));
}
})
}
})( jQuery );
However, it doesn't seem to be executed when I call it. Fiddle here: http://jsfiddle.net/DCRnU/
$(document).ready(function() {
$.fn.my_plugin();
});
What am I missing out on?
You're not calling the function properly.
If you had a div element in your HTML, you could call your function like this:
$(document).ready(function() {
$("div").my_plugin();
});
Example fiddle
That's because this line: return this.each
It's expect to get some iterable object.
But there is nothing to loop over it.
if you add something like this:
var array = [1];
$(array).my_plugin();
it'll be fine.
Related
I replicate the problem in simple plugin in jsfiddle: https://jsfiddle.net/5atn3010/1/
The idea is:
I have 2 selectors with a range of years (from 2005 to 2020)
When I select an option in the first selector, the selected value (for example 2010) is set in the second selector as the minimum value.
Finally the second selector is redrawn and only shows the new values (form 2010 to 2020)
This works, but with a big mistake. Not only changes the second select values, but the minimum value for the first selector also changes.
Why does it happen? How can I solve it?
;(function ( $, window, document, undefined ) {
"use strict";
var pluginName = "testing";
function Plugin( element, options ) {
this.element = element;
this.$element = $(element);
this.name = pluginName;
this.opts = $.extend({}, $.fn[pluginName].defaults, options);
this.$elements = {
year:null,
}
this.init(element,options);
}
Plugin.prototype = {
init: function () {
var me=this;
me.$elements.year=$("<select />").attr("name","year");
if (me.opts.css!=null) {
me.$elements.year.addClass(me.opts.css);
}
me.$elements.year.on("change",function() {
me.opts.onChange.call(me,me.$elements.year.val());
me._draw.call(me); //redraw me only for show error
});
me.$element.append(me.$elements.year);
me._draw();
},
_draw: function() {
var me=this;
var date_start=me.opts.date.start;
var date_end=me.opts.date.end;
me.$elements.year.find("option").remove();
for (var i=date_start;i<=date_end;i++) {
var option=$("<option/>").attr("value",i).text(i);
me.$elements.year.append(option);
}
},
setMin: function(min) {
this.opts.date.start=min;
this._draw();
}
}
$.fn[pluginName] = function(options) {
var param=arguments[1];
return this.each(function() {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
}
else if ($.isFunction(Plugin.prototype[options])) {
$.data(this, 'plugin_' + pluginName)[options](param);
} else {
$.error('Method ' + options + ' is not available');
}
});
};
$.fn[pluginName].defaults = {
date: {
start:2005,
end:2020
},
onSelect:function() {}
};
})( jQuery, window, document );
$().ready(function(){
$("#span1").testing({
onChange:function(min) {
console.log(min);
$("#span2").testing("setMin",min);
}
});
$("#span2").testing();
});
there are two issues with your code. First of all - you're not extending your options object recursively. To be short - you have two different opts objects that holds a reference to the same date object. I added some logging so you can understand what I'm talking about. You need to deep copy your options object.
Please, read carefully jQuery.extends page http://api.jquery.com/jquery.extend/
Secondly, you assign this.opts.date.start to a <option> value. It works okay for now but it won't work as expected if you will try to set a minimum date to selected date + N. It will concatenate N as a string instead of adding. I added some logs for this case as well.
http://jsfiddle.net/5atn3010/7/
I have a jQuery plugin, and I want to be able to change options on the fly, like this example: $('.element').pwstabs('options','effect',scale) or something simular to it. I tried adding update: function, tried adding Plugin.prototype.update, but still cant figure out how to do that :)
Here's the structure of the plugin:
;(function ($, window, document, undefined) {
var pluginName = "pwstabs",
defaults = {
effect: 'scaleout',
defaultTab: 1,
containerWidth: '100%',
tabsPosition: 'horizontal',
horizontalPosition: 'top',
verticalPosition: 'left',
responsive: false,
theme: '',
rtl: false,
controlls: false,
next: '',
prev: '',
first: '',
last: '',
auto: false,
play: '',
pause: ''
};
function Plugin(element, options) {
this.element = $(element);
this.$elem = $(this.element);
this.settings = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}
Plugin.prototype = {
init: function(){
// Here's the code for the plugin
}
};
$.fn[pluginName] = function ( options ) {
return this.each(function () {
new Plugin( this, options );
});
};
})(jQuery, window, document);
So now I use the plugin like:
$('.element').pwstabs({
effect: 'scalein',
defaultTab: 2
});
And when I click a button, i want to change effect to lets say scaleout. With code like:
$('.button').click(function(){
$('.element').pwstabs('options','effect','scalein');
});
So how do I implement this in the plugin?
Currently the only supported invocation pattern in that plugin is to send in an object literal containing the settings to overwrite the defaults. E.g.:
$('.element').pwstabs({
effect: 'scalein',
defaultTab: 2
});
That invocation pattern is defined in the following method:
$.fn[pluginName] = function ( options ) {
return this.each(function () {
new Plugin( this, options );
});
};
As you see, a dictionary of options is sent as the only parameter to the constructor function Plugin() to build the plugin and initialize it.
To support the invocation pattern you need, you would have to modify this method to support both invocation patterns (initialization with an object literal, but also invoking any method with more params, like your options setting method).
Here is an improved function that will handle both invocation patterns. In addition it will also store the instance of a plugin on an element, so you can access the existing settings etc. on subsequent invocations (e.g. settings changes) on the same element.
$.fn[pluginName] = function (options) {
// get the arguments
var args = $.makeArray(arguments),
after = args.slice(1);
return this.each(function () {
// check if there is an existing instance related to element
var instance = $.data(this, pluginName);
if (instance) {
if (instance[options]) {
instance[options].apply(instance, after);
} else {
$.error('Method ' + options + ' does not exist on Plugin');
}
} else {
// create the plugin
var plugin = new Plugin(this, options);
// Store the plugin instance on the element
$.data(this, pluginName, plugin);
return plugin;
}
});
}
This would allow you to invoke the plugin as requested:
$('.element').pwstabs('options','effect','slidedown');
However, this implies you have an 'options' method in the Plugin prototype, so make sure to add one:
Plugin.prototype = {
options: function (option, val) {
this.settings[option] = val;
},
// Constructing Tabs Plugin
init: function () {
// omitted code for brevity
}
}
As you see the options settings just sets the new option on the existing instance. Very simple and efficient. The new setting will be picked up by the click method handler and voila!
Here is a jsFiddle with example code in case you have trouble implementing what i was describing so far:
http://jsfiddle.net/7whs3u1n/6/
Update: I have much improved my answer to get rid of unneeded stuff, include more details and a full implementation that works (check the fiddle above) ;) i hope that this answers your question!
Adding statefulness to your plugin wasn't hard, but when you have spare time also check the alternative mechanism for writing stateful jQuery stateful plugins called jQuery widget factory:
http://learn.jquery.com/plugins/stateful-plugins-with-widget-factory/
In the future you can consider rewriting your plugin to use the widget factory. It would certainly make your code simpler ;)
Try this pattern
(function ($) {
var defaults = {
"text": "abcdefg",
}
, options = $.extend({}, defaults, options);
$.fn.plugin = function (options) {
var options = (function (opts, def) {
var _opts = {};
if (typeof opts[0] !== "object") {
_opts[opts[0]] = opts[1];
};
return opts.length === 0
? def
: typeof opts[0] === "object"
? opts[0] : _opts
}([].slice.call(arguments), defaults));
return $(this).text(options.text)
}
}(jQuery));
$(".results:eq(0)").plugin(); // return `defaults`
$(".results:eq(1)").plugin({"text":"gfedcba"}); // return `options`
$(".results:eq(2)").plugin("text", 123); // return `arguments` as `options`
(function ($) {
var defaults = {
"text": "abcdefg",
}
, options = $.extend({}, defaults, options);
$.fn.plugin = function (options) {
var options = (function (opts, def) {
var _opts = {};
if (typeof opts[0] !== "object") {
_opts[opts[0]] = opts[1];
};
return opts.length === 0
? def
: typeof opts[0] === "object"
? opts[0] : _opts
}([].slice.call(arguments), defaults));
return $(this).text(options.text)
}
}(jQuery));
$(".results:eq(0)").plugin(); // return `defaults`
$(".results:eq(1)").plugin({"text":"gfedcba"}); // return `options`
$(".results:eq(2)").plugin("text", 123); // return `arguments` as `options`
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="results"></div><br />
<div class="results"></div><br />
<div class="results"></div>
I'm working with iThemes Exchange for WordPress and I'm trying to modify the javascript file that controls certain aspects of the iThemes Exchange shopping cart checkout process.
The problem is I can't modify the plugin itself because any changes will be overwritten when the client updates their plugins. So I'm trying to modify the javascript plugin's updateStates() function so that the country label is automatically set to "United States".
I've tried modifying the plugin using jQuery.fn.itCountryStatesSync which is where the plugin is, but I must be doing something wrong. If it helps I'll include the plugin's javascript file.
/**
* jQuery Country States Sync Plugin by iThemes
* Plugin framework via <http://coding.smashingmagazine.com/2011/10/11/essential-jquery-plugin-patterns/>
*/
;(function ( $, window, document, undefined ) {
// Create the defaults once
var pluginName = 'itCountryStatesSync',
defaults = {
stateWrapper : '.it-exchange-state',
stateFieldID : '#it-exchange-address-state',
action : 'ite-country-states-update',
templatePart : '',
clearTextValue : false,
ajaxUrl : itExchangeAjaxCountryStatesAjaxURL,
adminPrefix : false,
autoCompleteState : false,
autoFocusState : false
};
// The actual plugin constructor
function Plugin( countrySelectElement, options ) {
// Set element
this.element = countrySelectElement;
// jQuery has an extend method that merges the
// contents of two or more objects, storing the
// result in the first object. The first object
// is generally empty because we don't want to alter
// the default options for future instances of the plugin
this.options = $.extend( {}, defaults, options) ;
// Country Field ID
this.options.countryFieldID = '#' + $(this.element).attr('id');
this._defaults = defaults;
this._name = pluginName;
this.init();
}
Plugin.prototype.init = function () {
// Place initialization logic here
// You already have access to the DOM element and
// the options via the instance, e.g. this.element
// and this.options
$(this.element).on('change', this.options, this.updateStates );
};
Plugin.prototype.updateStates = function( event ) {
var iteCountryStatesSyncPostData = {};
iteCountryStatesSyncPostData.ite_base_country_ajax = $(event.data.countryFieldID).val();
iteCountryStatesSyncPostData.ite_base_state_ajax = $(event.data.stateFieldID).val();
iteCountryStatesSyncPostData.ite_state_name_ajax = $(event.data.stateFieldID).attr('name');
iteCountryStatesSyncPostData.ite_action_ajax = event.data.action;
iteCountryStatesSyncPostData.ite_template_part_ajax = event.data.templatePart;
iteCountryStatesSyncPostData.ite_admin_prefix_ajax = event.data.adminPrefix;
iteCountryStatesSyncPostData.ite_clearTextValue = event.data.clearTextValue;
$.post(event.data.ajaxUrl, iteCountryStatesSyncPostData, function(response) {
if (response) {
var parentForm = $(event.data.countryFieldID).closest('form');
$(event.data.stateWrapper, parentForm).html(response);
if (event.data.autoCompleteState)
$(event.data.stateFieldID).filter('select').selectToAutocomplete();
if (event.data.autoFocusState) {
if ( $(event.data.stateWrapper).children('.ui-autocomplete-input').length !== 0 ) {
$(event.data.stateWrapper).children('.ui-autocomplete-input').focus();
} else {
$(event.data.stateFieldID).focus();
}
}
}
});
}
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName,
new Plugin( this, options ));
}
});
}
})( jQuery, window, document );
I have a basic plugin that populates an array within the plugin. How can I get that array via a method call with parameters. This is my first plugin so please go easy on me if this is a dumb question.
basic Plugin
(function($) {
$.fn.myPlugin = function() {
return this.each(function(){
tagArray = []; // my array that is populated
//code that does stuff to populate array
});
}
})(jQuery);
I would like to get the tagArray like so...
var arr = $('.className').myPlugin("getArray");
Where I can then use that array elsewhere. How can I accomplish this?
Thank you for any help.
I don't see why you would need the "getArray" parameter. In any case you need to define only 1 array and make your function return it:
(function($) {
$.fn.myPlugin = function() {
var tagArray = [];
this.each(function(){
// add something to tagArray
});
return tagArray;
}
})(jQuery);
That's a rather strange requirement, but an easy way to do that if there is only parameter would be something like :
(function($) {
$.fn.myPlugin = function(param) {
var tagArray = [],
elems = this.each(function(){
tagArray.push( $(this).text() ); // whatever you do ??
});
return param == 'getArray' ? tagArray : elems;
} // ^^ if the parameter is passed, return the array, otherwise the elems
})(jQuery);
FIDDLE
It's a bit hackish, but it works. You could also just return this.map(function() {... to always return an array etc, or read up on how to pass multiple parameters to a plugin and do different things etc. instead of the hardcoded check for 'getArray' used above.
Try
(function($) {
function Plugin($el, opts){
this.tagArray = [];
this.tagArray.push($el.attr('id')) //for testing the retuned instance
$el.data('myPlugin', this);
}
Plugin.prototype.getTagArray = function(){
return this.tagArray;
}
$.fn.myPlugin = function(opts) {
if($.type(opts) == 'string'){
var plugin = this.data('myPlugin');
return plugin[opts]();
}
return this.each(function(){
var $this = $(this);
new Plugin($this);
});
}
})(jQuery);
jQuery(function(){
$('#e1, #e2, #e3').myPlugin();
console.log($('#e1').myPlugin('getTagArray'))
console.log($('#e2').myPlugin('getTagArray'))
console.log($('#e3, #e1').myPlugin('getTagArray'))
})
Demo: Fiddle
I just finished writing a JQuery Plugin myself and here is the basic structure I settled on:
(function (window, document, $, undefined) {
//Local Methods
var methods = {
init : function(options){
//stuff you want to do when your plugin initializes i.e. when you do $('selector').myPlugin(options)
},
getArray: function(){
//your getArray method. Put your get array logic here
}
}
//Plugin Initialize
$.fn.myPlugin = function(args){
if ( methods[args] )
{
//execute JQuery Plugin Method
return methods[ args ].apply( this, Array.prototype.slice.call( arguments, 1 ));
}
else if ( typeof args === 'object' || ! args )
{
//Process JQuery Plugin Options
var opts = $.extend({}, $.fn.myPlugin.defaults, args);
var new_args = new Array(opts);
return methods.init.apply( this, new_args );
}
else
{
$.error( 'Method ' + args + ' does not exist on myPlugin' );
}
};
//Define Default Options
$.fn.myPlugin.defaults = {
option_1: '',
option_2: '',
option_n: ''
}
//API Methods
var M = $.myPlugin = function(){};
$.extend(M, {
getArray: function(){
return methods.getArray();
}
});
}(window, document, jQuery));
Doing this allows you to start your plugin like this (as usual):
$('.className').myPlugin(options);
and/or call your getArray function like this:
$.myPlugin.getArray();
I hope this helps you get closer to where you want to be.
I am learning JQuery. I have a need to create a custom control. This control is going to basically render some HTML. Sometimes, I want to just get the HTML. My hope is to use the following syntax:
// Put generated html inside of "myElement". "myElement" is a div element.
$("#myElement").myPlugin({ value: 10 });
// Retrieve the html that myPlugin would place of a div element.
// Basically, I want the javascript equivalent of a C# static function here.
// But I think the following approach is wrong:
var html = myPlugin().getHtml(10);
alert(html);
In an attempt to accomplish this, I'm using the following:
(function ($) {
$.fn.myPlugin = function (element, options) {
var defaults = { theValue: 0 }
var plugin = this;
plugin.settings = {};
var $element = $(element),
element = element;
plugin.init = function () {
plugin.settings = $.extend({}, defaults, options);
var html = createHtml(defaults.theValue);
$(element).html(html);
}
plugin.getHtml = function (v) {
return createHtml(v);
}
var createHtml(v) {
return "<span>" + v + "</span>";
}
})(jQuery);
I have two problems: 1) I can see that the HTML is being generated, but it does not appear to be added to the DOM. 2) I can't statically call the function. How can I make my function statically visible?
Thank you!
There are a few tweaks you need to make:
(function($) {
$.fn.myPlugin = function(options) {
var defaults = {
theValue: 0
},
settings = $.extend({}, defaults, options);
this.html($.myPlugin.getHtml(settings.theValue));
};
$.myPlugin = {
getHtml: function(value) {
return "<span>" + value + "</span>";
}
};
})(jQuery);
Usage:
$("#foo").myPlugin({ theValue: 10 });
var html = $.myPlugin.getHtml(10);
First off, you were never calling the init method, so nothing was going to work. It isn't going to be called automatically. Perhaps you were thinking of the jQueryUI widget factory?
Additionally, element doesn't get passed to the plugin, this refers the the element the plugin was called on. You only get passed options.
Finally, to create a static jQuery method, just attach it right to $.
Example: http://jsfiddle.net/xF2S6/