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>
Related
I have found nice jQuery plugin and it uses boiler plate template. Everything works fine, but I can't invoke internal function to get selected items.
The construction of this plugin is below:
(function ($, window, document) {
'use strict';
// constructor
var SearchableOptionList = function ($element, options) {
this.$originalElement = $element;
this.options = options;
this.metadata = this.$originalElement.data('sol-options');
};
// plugin prototype
SearchableOptionList.prototype = {
DATA_KEY: 'sol-element',
// default option values
defaults: {
...
},
// initialize the plugin
init: function () {
this.config = $.extend(true, {}, this.defaults, this.options, this.metadata);
...
return this;
},
//some functions
...
selectAll: function () {
...
},
deselectAll: function () {
...
},
getSelection: function () {
return this.$selection.find('input:checked');
}
};
// jquery plugin boiler plate code
SearchableOptionList.defaults = SearchableOptionList.prototype.defaults;
window.SearchableOptionList = SearchableOptionList;
$.fn.searchableOptionList = function (options) {
var result = [];
this.each(function () {
var $this = $(this),
$alreadyInitializedSol = $this.data(SearchableOptionList.prototype.DATA_KEY);
if ($alreadyInitializedSol) {
result.push($alreadyInitializedSol);
} else {
var newSol = new SearchableOptionList($this, options);
result.push(newSol);
setTimeout(function() {
newSol.init();
}, 0);
}
});
if (result.length === 1) {
return result[0];
}
return result;
};
}(jQuery, window, document));
Full code you can find on GitHub.
I try to invoke getSelection function like below:
// initialize sol
var s = $('#my-select').searchableOptionList({
maxHeight: '150px',
showSelectAll: true
});
s.selectAll();
I get an error:
TypeError: this.config is undefined
Is it possible to invoke functions using this template of boiler plate?
You can play around on jsfiddle
I believe line 1031 is the culprit
setTimeout(function() {
newSol.init();
}, 0);
Since the init is deferred the code is not ready when you call it right away. The esiest fix is to defer your call too, but there is no gurantee that it will be initialized.
setTimeout(function(){s.selectAll()},1000);
The better solution is to use the plugin's events to hook into when it has been initialized.
$('#my-select').searchableOptionList({
maxHeight: '150px',
events: {
onInitialized: function() {
this.selectAll();
}
}
});
fiddle
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 write this plugin and now i want add method to this plugin such as this
$.createMessage().removeMessage()
how can i do it?
my code is
$(function () {
$.extend({
createtext: function (options) {
var setting = {
holder: "",
text: "",
}
if (options != null) {
$.extend(setting, options)
}
var $this = $(setting.holder)
$this.find("div#CreatetextHolder").remove()
$this.append("<div id='CreatetextHolder'><span></span><p class='Createtext'>" + setting.text + "</p></div>")
$this.find("div#CreatetextHolder").fadeIn('slow')
}
})
})
thank you for your help
$(selector).createMessage().removeMessage() would require you to write two plugins - one for 'create' and the other for 'remove'.
It's far better to do everything in one plugin and you can do so by targeting the syntax ...
$(selector).createMessage('remove');
Then it's a matter of testing options in the plugin code, and branching accordingly.
Currently you test if (options != null) assuming options to be a javascript plain object and that the only action is initianisation.
But with my suggestion to allow $.createMessage('remove'), you need to perform more extensive testing/branching depending on what parameter(s) are actually passed.
For example:
$(function () {
$.extend({
createtext: function ( method, options ) {
var settings = {
holder: "",
text: ""
};
var methods = {
'init': function(options) {
var _settings = $.extend({}, settings, options);//this leaves `settings` unaffected and available for reuse in future inits.
//initialize here
},
'remove': function() {
//uninitialize here
}
}
// These tests allow `init' to be passed explicitly,
// or assumed if an options object is the only argument.
// Otherwise, a method such as 'remove' may be passed,
// with or without further parameters.
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 in jQuery.createtext');
}
}
});
});
It is the first time I write a jQuery plugin without a tutorial. Now (September 28 2014), the jQuery site doesn't work (I don't know why), so I cannot find any resource there.
Below is part of my plugin that reports errors:
$(function($){
$.fn.dialog = function(command, options) {
var opts = $.extend( {}, $.fn.dialog.defaults, options );
//code
$.fn.dialog.handleCancel = function() {
};
$.fn.dialog.handleAccept = function() {
};
return this;
};
$.fn.dialog.defaults = {
// some other props
onCancel: $.fn.dialog.handleCancel(),
onAccept: $.fn.dialog.handleAccept()
};
// code
}(jQuery));
When I call the plugin ($("#dialog1").dialog(/*..*/)), the browser console, shows the following:
Uncaught TypeError: undefined is not a function
The error is on the line with onCancel: $.fn.dialog.handleCancel().
How can I access these methods, and where should them be? (I also want them to have access to $(this) <- for the dialog itself)
Your handleCancel and handleAccept functions are not initialized until you call the $.fn.dialog function. Therefore, they are undefined when you set the dialogs defaults.
Insert this code prior to $.fn.dialog.defaults:
$.fn.dialog();
Try rearranging blocks within the piece , adding a filter , to prevent both handleCancel and handleAccept being called by default; e.g.,
(function($){
$.fn.dialog = function(command, options) {
var $el = this;
// access , pass arguments to methods
$.fn.dialog.handleCancel = function(c) {
$el.html(c + "led")
};
$.fn.dialog.handleAccept = function(a) {
$el.html(a + "ed")
};
// call `handleCancel` or `handleAccept` ,
// based on `command`
$.fn.dialog.defaults = {
// some other props
onCancel: command === "cancel"
? $.fn.dialog.handleCancel(command)
: null,
onAccept: command === "accept"
? $.fn.dialog.handleAccept(command)
: null
};
var opts = $.extend( {}, $.fn.dialog.defaults, options );
//code
return this;
};
// code
}(jQuery));
$("button").on("click", function(e) {
$("#result").dialog(e.target.id)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button id="accept">accept</button><button id="cancel">cancel</button><br />
Result: <div id="result"></div>
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.