Below is my my plugin:
(function($) {
$.fn.myPlugin = function(options) {
var opt = $.extend({}, $.fn.myPlugin.defaults, options);
this.foo()
{
alert('test');
}
}
$.fn.myPlugin.defaults = {
};
});
Now I want to extend it without touching the original plugin i.e I want the full feature of existing plugin + the new features which I want. Below are the new things I need:
First:
Name of the new plugin "myPlugin2"
Second:
The "foo" function of the existing plugin should be overridden in the new plugin with this:
function foo() {
alert('test2');
}
Third:
I need to add one more method to my new plugin say function foo2(){} .
Can you help me in achieving this?
You need to define your default name and foo events in your defaults declaration:
$.fn.myPlugin.defaults = {
name: 'test',
onFoo: function() {
alert(this.name);
},
onFoo2: function() {
// your default behaviour for foo2
}
};
Then, when someone calls your plugin, they can override the defaults, in this case name:
$("#myControl").myPlugin({
name: 'test2'
});
Note that they don't need to override onFoo, because it will display an alert with test2. Anyway, if they need to override it to do something different, then they should:
$("#myControl").myPlugin({
name: 'test2',
onFoo: function() {
alert('onFoo overrired');
},
onFoo2: function() {
alert('onFoo2 overrired');
}
});
In your plugin, you invoke the foo methods as
(function($) {
$.fn.myPlugin = function(options) {
var opt = $.extend({}, $.fn.myPlugin.defaults, options);
// if onFoo is defined then call it
if (opt.onFoo) {
opt.onFoo();
}
// if onFoo2 is defined then call it
if (opt.onFoo2) {
opt.onFoo2();
}
}
$.fn.myPlugin.defaults = {
name: 'test',
onFoo: function() {
alert(this.name);
},
onFoo2: function() {
// your default behaviour for foo2
}
};
});
You should use this technique for public methods/properties that you want to expose to the users of your plugin.
I didn't tested but should work
Edit
You need to check if the event is set before calling it:
// if onFoo is defined (not null) then call it
if (opt.onFoo) {
opt.onFoo();
}
You are setting already an event for onFoo and onFoo2, but the user of your plugin might choose to disable it:
$("#myControl").myPlugin({
onFoo: null
});
In this case, although you have defined an onFoo event, the user of your plugin decided to ignore it, by setting it to null. So, even though you have defined an event, you never know what others will do with it, therefore it's better to be on the safe side and check for nullity.
Once again, you need to be careful with what you expose to the end user, because setting/unsetting events should not break the basic functionality of your plugin
If this is any decently coded plugin, you shouldn't be able to alter it's methods. It should of made anything which isn't meant to be invoked an internal function i.e.:
$.fn.oldPlugin = function() {
var foo = function() {
alert('old code');
};
};
There is no way to invoke foo or overwrite it.
Should you not need to change any of the methods/functions then you can use $.extend($.fn.pluginName, {/*your methods/properties*/};
What it all really comes down to is:
How the plugin you want to extend is coded
If you want to overwrite or just extend on it's functionality
Related
With `arguments.callee deprecated I would like to find al alternative approach to extend a jQuery plugin I created to execute one of its built in methods from the outside.
My sample jQuery plugin:
(function($) {
$.myPlugin = function(options) {
// normal var and methods
// this is to execute a method from outside
arguments.callee.close = function() {
// to do when this is called
}
}
}(jQuery))
The plugin would be used for it’s normal use with this
$(selector).myPlugin();
And this is to trigger the “close” method inside the plugin
$(selector).myPlugin.close()
I found an answer to this and have this to share:
(function($) {
function myPlugin(options) {
// normal var and methods: do whatever I want...
myPlugin.close = myPlugin.close.bind(this);
}
myPlugin.close = function() {
// do this thing
}
$.fn.myPlugin = myPlugin;
}(jQuery));
This allows me to call the close method with this:
$(selector).myPlugin.close();
How should I best go about overriding a JavaScript class method when it has been set up as per below. In this snippet, if I want to override the _other method from another JS file, loaded after this one, what is the correct way to go about it?
var review = {};
"use strict";
(function ($) {
review.list = {
_init: function () {
// The code I want to leave intact
},
_other: function () {
// The code I want to override
},
init: function () {
$(document).ready(function () {
review.list._init();
review.list._other();
});
}
};
review.list.init();
})(jQuery);
You can just assign to review.list._other. If you want to have access to the previous version, grab that first:
var oldOther = review.list._other;
review.list._other = function() {
// Your new code here, perhaps calling oldOther if you like
console.log("The new other code ran.");
};
Example:
// The original file
var review = {};
"use strict";
(function($) {
review.list = {
_init: function() {
// The code I want to leave intact
},
_other: function() {
// The code I want to override
},
init: function() {
$(document).ready(function() {
review.list._init();
review.list._other();
});
}
};
review.list.init();
})(jQuery);
// Your file after it
(function($) {
var oldOther = review.list._other;
review.list._other = function() {
// Your new code here, perhaps calling oldOther if you like
console.log("The new other code ran.");
};
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You're actually quite lucky it was written that way. It could easily have been written such that you couldn't override _other at all...
Slightly off-topic, but you've asked below:
Actually, does this class structure look reasonably sensible to you? Trying to dip toes into more OOP JS
I don't know your design constraints, so take anything that follows with a grain of salt... I should note that there's no "class" there at all (neither in the ES5 and earlier sense nor the ES2015 and later sense), just an object. (Which is fine.) But it looks like _init and _other are meant to be private; they could be genuinely private instead of pseudo-private without any cost — except then you wouldn't be able to override _other! :-) Separately, I would allow the overall controlling code to determine when the initialization happened instead of doing it on ready. (Separately, on a pure style note, I don't hold at all with this two-spaces-indentation nonsense so many of the l33t types seem to be promoting. If your code is so deeply nested that using only two spaces for an indent is necessary, it needs refactoring; four spaces is a good solid clear indent, in my view, without being so big it pushes your code off the right-hand side.)
So something like this if ES5 is required:
(function($) {
var list = {
init: function() {
_init();
_other();
}
};
function _init () {
// Can use `list` here to refer to the object
}
function _other() {
// Can use `list` here to refer to the object
}
review.list = list;
})(jQuery);
...but again, that makes it impossible (well, unreasonable) to override _other.
Or this if ES2015 and above is okay (for code this short, the differences are quite minor):
(function($) {
let list = {
init() {
_init();
_other();
}
};
function _init () {
// Can use `list` here to refer to the object
}
function _other() {
// Can use `list` here to refer to the object
}
review.list = list;
})(jQuery);
Just add your new override below... It will work...
var review = {};
"use strict";
(function($) {
review.list = {
_init: function() {
console.log('I\'m init')
},
_other: function() {
//This original will be overridden
console.log('Original')
},
init: function() {
$(document).ready(function() {
review.list._init();
review.list._other();
});
}
};
review.list.init();
})(jQuery);
review.list._other = function() {
console.log('overridden')
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
The concept is to have 2 plugins one for form and another for button. I want to bind all forms in my page to JQuery plugin that will handle some jobs let say that this is my plugin
$.fn.PluginForm = function (Options) {
var o = jQuery.extend({
SomeOption: 1
}, Options);
var Validate = function(){
if(o.SomeOption == 1) return true;
else return false;
};
$(this).on('submit', function(e) {
e.preventDefault();
//some code here
});
};
The form actually doesn’t have button in my case the post is triggered from another control. This is because of the structure of the application I want to build. The button plugin is:
$.fn.PluginButton = function (Options) {
var o = jQuery.extend({
Actions: [],
FormID: ''
}, Options);
$(this).click(function(){
var Form = $('#' + o.FormID);
if(Form.length > 0 && Form.PluginForm.Validate()) {
Form.submit();
//do something
}
else{
//do something else
}
});
};
What I want to succeed is to invoke the validation function on the Form element but I don’t want to invoke another instance of the PluginForm. Something like $('#' + o.FormID).PluginForm.Validate()
All this must be as plugin because there will be a lot of forms in the same page and a lot of buttons. Also there will be a lot of buttons that can invoke submit on the same form but with different options. That’s why I want to invoke one time the instance of the form. Also the controls that will be validated will be passed as parameter in the options of the PluginForm. Something like this $('#' + o.FormID).PluginForm({ Action: ‘Validate’ }) is not an option because will lose the initial parameters of the PluginForm.
You can save the plugin instance in the .data() structure on the element, and then call it back. Most of plugins use it that way.
/*!
* jQuery lightweight plugin boilerplate
* Original author: #ajpiano
* Further changes, comments: #addyosmani
* Licensed under the MIT license
*/
// 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).
// Create the defaults once
var pluginName = "defaultPluginName",
defaults = {
propertyName: "value"
};
// The actual plugin constructor
function Plugin( element, options ) {
this.element = element;
// 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) ;
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
// you can add more functions like the one below and
// call them like so: this.yourOtherFunction(this.element, this.options).
},
yourOtherFunction: function(el, options) {
// some logic
}
};
// 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 );
taken from: https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.basic.plugin-boilerplate.js
also there are more jquery plugin design patterns that may fit more for your plugin at http://jqueryboilerplate.com/.
I'm trying to keep a jquery plugin I'm working on configurable, and someone what maintainable, by keeping the various functions I use short and relatively easy to test.
To this I'm using some jQuery plugin code, based around the jQuery boilerplate, Addy Osmani's Lightweight Start, to have a plugin where I can pass in overrides, and compose existing functions from a series of small ones.
However, I'm having some trouble working out how to access functions I've declared from within a deferred done() callback, without declaring all the function code inside the done() function call again.
Is there a recommended pattern for making these functions available when using a prototype based approach like outlined in the boilerplate?
(function($, window, document, undefined) {
var pluginName = 'myModule';
function myModule(element, options) {
this.element = element;
// allow override of defaults
this.options = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
// calling the init() function defined below
this.init();
}
myModule.prototype = {
init: function() {
// add listeners for clicks on the element, and trigger some
// behaviour defined in fetchScore()
$(this.element).click(function() {
that.fetchScore();
return false;
});
},
handySuccessFunction: function() {
// some handy DOM manipulation stuff,
// kept out the main fetchScore function,
// ideally to make it more testable and readable
},
handyFailingFunction: function() {
// same again for failing case
},
fetchScore: function(authToken) {
$.getJSON(this.options.endpoint, {
apiKey: this.options.apiKey,
otherParam: this.options.otherParam,
token: authToken
})
.done(function(json) {
// I want to call the handySuccessFunction() here,
// but I have no access to myModule
})
.fail(function(jqxhr, textStatus, error) {
// Likewise I want to call the handyFailingFunction() here
});
}
}
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations.
// We store a reference to the
$.fn[pluginName] = function(options) {
return this.each(function() {
if (!$.data(this, "plugin_" + pluginName)) {
$.data(this, "plugin_" + pluginName,
new pluginName(this, options));
}
});
}
})(jQuery, window, document);
Here's my expected usage:
jQuery(document).ready(function($) {
// console.log('clicking the popup');
$('#elementToAttachTo').myModule();
// clicking on this to trigger the fetchScore
// behaviour in myModule
$('#elementToAttachTo').click();
})
You should use "bind" to callback function in "done", set it context ("this") to myModule instance, where this function was declarated.
There are several ways.
You can use navtive Function.prototype.bind() method, which works in modern browsers
You can use jQuery $.proxy function.
So
myModule.prototype.fetchScore = function(authToken) {
$.getJSON(this.options.endpoint, {
apiKey: this.options.apiKey,
otherParam: this.options.otherParam,
token: authToken
})
.done(function(json) {
this.handySuccessFunction();
}.bind(this))
.fail($.proxy(function(json) {
this.handyFailingFunction();
}, this))
;
};
I want to create jQuery plugin with config (for example plugin myplugin).
Than call $(elem).myplugin(config); After that I want to call methods from this plugin like $(elem).myplugin().method() with already stored config.
My offer is something like that:
(function($) {
$.fn.myplugin = function(options) {
var $this = $(this);
var getOptions = function() {
return $this.data('myplugin');
};
var initOptions = function(opt) {
$this.data('myplugin', opt);
};
var setOption = function(key, value) {
$this.data('myplugin')[key] = value;
}
var updateBorderWidth = function() {
$this.css('border-width',
getOptions().borderWidth * getOptions().coeficient);
};
var init = function(opt) {
initOptions(opt);
updateBorderWidth();
}
function changeBorder(width) {
setOption('borderWidth', width)
updateBorderWidth();
}
if(options) {
init(options);
}
return {
changeBorder : changeBorder
};
}
})(jQuery);
And usage:
$(function() {
var item1 = $('#test1').myplugin({ coeficient: 1, borderWidth: 1 });
var item1 = $('#test2').myplugin({ coeficient: 2, borderWidth: 1 });
$('#btn').click(updateBorder);
});
function updateBorder() {
$('#test1').myplugin().changeBorder($('#inpt').val());
$('#test2').myplugin().changeBorder($('#inpt').val());
}
Example: http://jsfiddle.net/inser/zQumX/4/
My question: is it a good practice to do that?
May be it's incorrect approach. Can you offer better solution?
Edit:
After searching for threads on jQuery plugin template I found these Boilerplate templates (updated) which are more versatile and extensive designs than what I've offered below. Ultimately what you choose all depends on what your needs are. The Boilerplate templates cover more use cases than my offering, but each has its own benefits and caveats depending on the requirements.
Typically jQuery plugins either return a jQuery object when a value is passed to them as in:
.wrap(html) // returns a jQuery object
or they return a value when no parameter is passed in
.width() // returns a value
.height() // also returns a value
To read your example calling convention:
$('#test1').myplugin().changeBorder($('#inpt').val());
it would appear, to any developer who uses jQuery, as though two separate plugins are being utilized in tandem, first .myplugin() which one would assume will return a jQuery object with some default DOM maniplulation performed on #test1, then followed by .changeBorder($('#inpt').val()) which may also return a jQuery object but in the case of your example the whole line is not assigned to a variable so any return value is not used - again it looks like a DOM manipulation. But your design does not follow the standard calling convention that I've described, so there may be some confusion to anyone looking at your code as to what it actually does if they are not familiar with your plugin.
I have, in the past, considered a similar problem and use case to the one you are describing and I like the idea of having a convenient convention for calling separate functions associated with a plugin. The choice is totally up to you - it is your plugin and you will need to decide based on who will be using it, but the way that I have settled on is to simply pass the name of the function and it's parameters either as a separate .myplugin(name, parameters) or in an object as .myplugin(object).
I typically do it like so:
(function($) {
$.fn.myplugin = function(fn, o) { // both fn and o are [optional]
return this.each(function(){ // each() allows you to keep internal data separate for each DOM object that's being manipulated in case the jQuery object (from the original selector that generated this jQuery) is being referenced for later use
var $this = $(this); // in case $this is referenced in the short cuts
// short cut methods
if(fn==="method1") {
if ($this.data("method1")) // if not initialized method invocation fails
$this.data("method1")() // the () invokes the method passing user options
} else if(fn==="method2") {
if ($this.data("method2"))
$this.data("method2")()
} else if(fn==="method3") {
if ($this.data("method3"))
$this.data("method3")(o) // passing the user options to the method
} else if(fn==="destroy") {
if ($this.data("destroy"))
$this.data("destroy")()
}
// continue with initial configuration
var _data1,
_data2,
_default = { // contains all default parameters for any functions that may be called
param1: "value #1",
param2: "value #2",
},
_options = {
param1: (o===undefined) ? _default.param1 : (o.param1===undefined) ? _default.param1 : o.param1,
param2: (o===undefined) ? _default.param2 : (o.param2===undefined) ? _default.param2 : o.param2,
}
method1 = function(){
// do something that requires no parameters
return;
},
method2 = function(){
// do some other thing that requires no parameters
return;
},
method3 = function(){
// does something with param1
// _options can be reset from the user options parameter - (o) - from within any of these methods as is done above
return;
},
initialize = function(){
// may or may not use data1, data2, param1 and param2
$this
.data("method1", method1)
.data("method2", method2)
.data("method3", method3)
.data("destroy", destroy);
},
destroy = function(){
// be sure to unbind any events that were bound in initialize(), then:
$this
.removeData("method1", method1)
.removeData("method2", method2)
.removeData("method3", method3)
.removeData("destroy", destroy);
}
initialize();
}) // end of each()
} // end of function
})(jQuery);
And the usage:
var $test = $('#test').myplugin(false, {param1: 'first value', param2: 'second value'}); // initializes the object
$test.myplugin('method3', {param1: 'some new value', param2: 'second new value'}); // change some values (method invocation with params)
or you could just say:
$('#test').myplugin(); // assume defaults and initialize the selector
Passing parameters to javascript via data attributes is a great pattern, as it effectively decouples the Javascript code and the server-side code. It also does not have a negative effect on the testability of the Javascript code, which is a side-effect of a lot of other approaches to the problem.
I'd go as far as to say it is the best way for server-side code to communicate with client-side code in a web application.