Override/Intercept all jQuery functions - javascript

I was trying to override all jQuery functions to perform an action whenever jQuery was called (for auditing purposes or other reasons). However I probably need to get the same parameters a function receives in order to make the following code work:
function __wrapjQuery () {
var wrapper = this;
alert('Wrapping');
var functions = Object.getOwnPropertyNames($.fn).filter(function (p) {
return ( typeof( $.fn[p] ) === 'function');
});
for (var i = 0; i < functions.length; i++) {
wrapper.oldTempjQueryFunction = $.fn[functions[i]];
$.fn[functions[i]] = function () {
var self = this;
self.wrappedFunction = wrapper.oldTempjQueryFunction;
var returnVar = self.wrappedFunction.call(this);
alert('jQuery was called');
return returnVar;
}
}
}
$().ready(self.__wrapjQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myDiv" style="background-color:#FF0000">
ELEMENT
</div>
<button onclick="$('#myDiv').remove()">Remove Element</button>
However the code below, where I specify the function to override (remove) ,works (maybe because of the parameters):
function __wrapjQuery () {
var wrapper = this;
alert('Wrapping');
wrapper.wrappedRemoveFunction = $.fn.remove;
$.fn.remove = function () {
var self = this;
var returnVar = wrapper.wrappedRemoveFunction.call(this);
alert('jQuery was called to remove');
return returnVar;
}
}
$().ready(self.__wrapjQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myDiv" style="background-color:#FF0000">
ELEMENT
</div>
<button onclick="$('#myDiv').remove()">Remove Element</button>
How would I replace all functions (even those with parameters, like each) and keep jQuery working normally? Let's say I want to audit the most used methods from jQuery.

You can wrap all jQuery object methods to collect whatever statistics you want with code like this:
// place this right after jQuery and any jQuery plugins are installed
(function($) {
Object.getOwnPropertyNames($.fn).filter(function (p) {
return ( typeof( $.fn[p] ) === 'function' && p !== "init");
}).forEach(function(funcName) {
var orig = $.fn[funcName];
$.fn[funcName] = function() {
console.log(funcName + " jQuery method called");
return orig.apply(this, arguments);
}
});
})(jQuery);
Working demo here: https://jsfiddle.net/jfriend00/7Ls0qkvb/
If you also wanted to include static methods, you could do a similar enumeration of the methods on the jQuery object.
Note, because $.fn.init() is called as a constructor and thus needs different treatment in how it is handled (you can't use .apply() with a constructor), I bypassed recording that method. I have not searched through jQuery to find any other methods called as a constructor, but they would likely need special treatment also.
Here's a more advanced version that can even log .init() and any other method called as a constructor: https://jsfiddle.net/jfriend00/uf531xzb/. It detects if a method is called with new and acts accordingly.

How would I replace all functions (even those with parameters, like
each) and keep jQuery working normally?
You can retrieve jquery-migrate-1.4.1.js plugin, include lines 354-373 for ability to use utilize deprecated jQuery.sub().
jQuery.sub() Returns jQuery
Description: Creates a new copy of jQuery whose properties and methods
can be modified without affecting the original jQuery object.
// https://code.jquery.com/jquery-migrate-1.4.1.js, lines 354-373
jQuery.sub = function() {
function jQuerySub(selector, context) {
return new jQuerySub.fn.init(selector, context);
}
jQuery.extend(true, jQuerySub, this);
jQuerySub.superclass = this;
jQuerySub.fn = jQuerySub.prototype = this();
jQuerySub.fn.constructor = jQuerySub;
jQuerySub.sub = this.sub;
jQuerySub.fn.init = function init(selector, context) {
var instance = jQuery.fn.init.call(this, selector, context, rootjQuerySub);
return instance instanceof jQuerySub ?
instance :
jQuerySub(instance);
};
jQuerySub.fn.init.prototype = jQuerySub.fn;
var rootjQuerySub = jQuerySub(document);
// migrateWarn( "jQuery.sub() is deprecated" );
return jQuerySub;
};
// do stuff with copy of jQuery
var __wrapjQuery = jQuery.sub();
__wrapjQuery.pluginName = "wrapper";
__wrapjQuery.fn.remove = function() {
console.log(__wrapjQuery.fn.remove, $.fn.remove);
alert(__wrapjQuery.pluginName);
}
__wrapjQuery(".wrapper").click(function() {
__wrapjQuery(this).remove()
});
$(".jquery").click(function() {
console.log($.fn.remove, __wrapjQuery.fn.remove);
$(this).remove();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="wrapper">wrapper <code>.remove()</code>
</button>
<button class="jquery">jQuery <code>.remove()</code>
</button>

Related

How can I insert an argument like forEach, reduce and the like does?

I'm trying to reinvent the wheel, sort of.. Just messing around trying to remake some jquery functions.. I've come this far
var ye = function (ele) {
if (ele[0] == "#")
{
return document.getElementById(ele.slice(1));
}
else if (ele[0] == ".")
{
// returns an array, use index
return document.getElementsByClassName(ele.slice(1));
}
else
{
// also returns an array
return document.getElementsByTagName(ele);
}
}
but how can I use this element as a parameter in a function in the 'ye' prototype. For example, if I wanted to make fontsize how could I get the dom element like here:
ye.prototype.fontSize = function (ele)
{
ele.style.fontSize = "30px";
}
Just to add a bit to make the title relevant.. forEach inserts three arguments into the callback function, just like I want ye to insert ele into the fontSize function.
Just messing around trying to remake some jquery functions...
...but how can I use this element as a parameter in a function in the 'ye' prototype..
Here is a very crude and simple way to start...
Create a function with a property called elems which is an array and will store the selected DOM elements.
Like this:
var oye = function() { this.elems = []; };
On its prototype, you can create your custom functions which you want to expose. e.g. the function fontSize (as in your question), iterate over the elems array property that we created earlier changing the font size of each DOM element stored in. this points to the instance which is calling this function which we will ensure to be of type oye later on. To enable chaining, we simply return itself via this.
Like this:
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
Now create the selector function called ye. This serves the purpose of selecting the DOM elements, storing them in the elems array property of a new instance of oye class, and return the instance. We call the slice of the array prototype to convert the nodeList to an array.
Like this:
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
Now start using it in your code. Just like jQuery, you can use ye to select and then call your custom functions.
Like this:
ye("#elem1").fontSize('30px');
Just like jQuery, you can also chain multiple custom functions as shown in the complete working example below:
ye("P").fontSize('24px').dim(0.4);
Next step: Remember this is just a very crude example. You can now proceed to club the step 1 and 2 into a single call using the init pattern returning the new object from the selector function itseld. Learn more about Javascript and best practices.
Here is a sample working demo:
var oye = function() { this.elems = []; };
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
oye.prototype.dim = function(value) {
return this.elems.forEach(function(elem) {
elem.style.opacity = value;
});
return this;
};
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
ye("#elem1").fontSize('30px');
ye(".elem2").fontSize('20px');
ye("P").fontSize('24px').dim(0.4);
<div>This is normal text.</div>
<div id="elem1">size changed via id.</div>
<div class="elem2">size changed via class.</div>
<div class="elem2">size changed via class.</div>
<p>size changed and dimmed via tag name</p>
<p>size changed and dimmed via tag name</p>
Regarding your question, I may think you're new to JavaScript, or not familiar with its basic concepts. I'm not sure reinventing the wheel is a good thing in such conditions.
Since you've cited jQuery, you can have a look at its source code to understand how it works under the hood:
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L17-L23
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L38-L81
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core/init.js#L19-L114
Having that said, I would have done something like this:
var ye = function ( ele ) {
return new ye.prototype.init(ele);
};
ye.prototype.init = function( ele ) {
this._elements = [].slice.call(document.querySelectorAll(ele));
return this;
};
ye.prototype.forEach = function( fn ) {
this._elements.forEach(fn);
return this;
};
ye.prototype.fontSize = function( fontSizeValue ) {
this.forEach(function (ele) {
ele.style.fontSize = fontSizeValue;
});
return this;
};
The associated usage is as follow:
var myCollection = ye('.someClassName');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
myCollection.fontSize('45px');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
Use ye function calling before setting style, something like:
ye.prototype.fontSize = function(ele) {
ye(ele).style.fontSize = '30px';
}
returned object should be richer, like that:
var baseObject = {
// Will be used for the element:
element: null,
width: function(){ return this.element.getwidth(); /* or anything similar*/ }
// ... Further methods
}
and then in your ye function:
var ye = function (ele) {
var yeElem = clone(baseObject); // See comment below!!
if (ele[0] == "#") { yeElem.element = document.getElementById(ele.slice(1)); }
else if (ele[0] == "."){ /*...*/ }
else { /*...*/ }
return yeElem;
}
This way the new element has built in methods.
As for the clone() method used, it doesn't exist but you have to use some clone method.
I recommend Loadsh's _.cloneDeep() (here).

jQuery plugin object looses variable?

I'm developing a simple plugin with jQuery. As I see it I need to do it like this:
(function($) {
var somePrivateFn = function() {
alert(this.x);
}
$.fn.myPlugin = function() {
if(typeof arguments[0] === "string") {
var fnargs = Array.prototype.slice(arguments, 1);
// pluginData will be undefined
var pluginData = this.pluginData;
somePrivateFn.apply(pluginData, fnargs);
}
return this.each(function() {
var data = this.pluginData = {};
data.x = 1;
};
}
})(jQuery);
Of course my plugin needs to store some variables for the jquery object it's working on. To do this I just place an additional variable "pluginData" at the jQuery object itself. The problem is it is not accessible later. How to deal with it? What's the best approach to do this?
Pass the object when you run the plugin. $('div').myPlugin(a_data_object) It will persist to within the this.each loop.
This var options=$.extend({}, pluginData); will keep the data in one iteration from passing through to the next.
If you want to manipulate then store that data on the element you would use $.data().
(function($) {
console.clear();
var somePrivateFn = function(data) {
console.log(data.x);
}
$.fn.myPlugin = function(pluginData) {
// send to another func
somePrivateFn(pluginData);
// use on each div
return this.each(function(i) {
var options=$.extend({}, pluginData);
// add some example data
options.y = "i am y";
options.iteration=i;
options.text=$(this).text();
// store it on the element
$(this).data('pluginData', options);
});
}
// run the plugin on div elements
$('div').myPlugin({x:"i am x"});
// check out the data later
$('div').each(function(){
console.log($(this).data('pluginData'));
});
})(jQuery);
Example: http://jsfiddle.net/xXt5W/1/
$.data() docs: https://api.jquery.com/jQuery.data/

Javascript namespace and public functions

I'm writing a jQuery plugin to allow the initializing of multiple, single file upload fields (using Fine Uploader) in a specified form.
Ultimately I would like the form to know that it has these single uploaders attached to it, so I can run some validations, etc before manually starting the file uploads and submitting the form.
My ideal initialization code would look like this:
var form = $("form");
form.uploader();
form.addUploader({
element: '.uploader_one'
});
form.addUploader({
element: '.uploader_two'
});
So far the plugin I wrote to make this happen looks like:
(function($) {
var Uploader = function(form){
addUploader = function() {
// Initialize Fine Uploader
}
$(form).submit(function(e) {
// Run validations, then process uploaders
$(this).submit();
}
}
$.fn.uploader = function(options) {
var uploader = new Uploader(this);
};
})(jQuery);
Most of this works, except that the addUploader function is not publicly accessible.
Might I be going about this the wrong way? Any help would be much appreciated!
You would need to make the addUploader a member of the object to make it accessible through the object (instead of as a global variable disconnected from the object):
this.addUploader = function() {
The plugin has to do something with the object so that it becomes accessible, for expample returning it:
$.fn.uploader = function(options) {
return new Uploader(this);
};
Now you can get the object from the plugin and use it:
var form = $("form");
var upl = form.uploader();
upl.addUploader({
element: '.uploader_one'
});
upl.addUploader({
element: '.uploader_two'
});
You should follow the general practice for developing plugins. A suggested structure for your plugin is something like:
(function($) {
var methods = {
init: function (options) {
var data = $(this).data("uploader-plugin");
if (!data) {
$(this).on("submit", function (e) {
});
$(this).data("uploader-plugin", new Uploader(options));
}
},
add: function (options) {
// Use `options.element`
$(this).data("uploader-plugin").elements.push(options.element);
console.log($(this).data("uploader-plugin"));
}
};
var Uploader = function (opts) {
this.whatever = opts.a;
this.elements = [];
};
$.fn.uploader = function(method) {
var args = arguments;
return this.each(function () {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(args, 1));
} else if (typeof method === "object" || !method) {
return methods.init.apply(this, args);
} else {
$.error("Method " + method + " does not exist on jQuery.uploader");
}
});
};
})(jQuery);
DEMO: http://jsfiddle.net/WyGqc/
And you will call it like this:
var form = $("form");
form.uploader({
a: "whatever";
});
form.uploader("add", {
element: ".uploader_one"
});
It actually applies it to all selected elements from the original selector, and continues chaining. It also follows the normal convention for plugin use - meaning, you call the plugin name (uploader) with different parameters to do different things.

How to use prototype for custom methods and object manipulation

Honestly, I am trying to understand JavaScript prototypes and I'm not making much progress. I am not exactly sure how to explain what I am trying to do, except to say that in part my end goal is to learn how to traverse the DOM similar to jQuery and to add custom methods to manipulate particular elements being accessed.
EDIT : The code below has been updated to reflect concepts I have learned from the answers received so far, and to show where those fall short of what I am looking to accomplish.
function A(id) {
"use strict";
this.elem = document.getElementById(id);
}
A.prototype.insert = function (text) {
"use strict";
this.elem.innerHTML = text;
};
var $A = function (id) {
"use strict";
return new A(id);
};
var $B = function (id) {
"use strict";
return document.getElementById(id);
};
function init() {
"use strict";
$A('para1').insert('text goes here'); //this works
$A('para1').innerHTML = 'text goes here'; //this does not work
console.log($A('para1')); //returns the object A from which $A was constructed
console.log($B('para1')); //returns the dom element... this is what I want
/*I want to have $A('para1').insert(''); work and $A('para1').innerHTML = '';
work the same way that $B('para1').innerHTML = ''; works and still be able
to add additional properties and methods down the road that will be able
act directly on the DOM element that is contained as $A(id) while also
being able to use the properties and methods already available within
JavaScript*/
}
window.onload = init;
Where possible please add an explanation of why your code works and why you believe it is the best possible method for accomplishing this.
Note: The whole purpose of my inquiry is to learn this on my own... please do not suggest using jQuery, it defeats the purpose.
var $ = function(id) {
return new My_jquery(id);
}
function My_jquery(id) {
this.elem = document.getElementById(id);
}
My_jquery.prototype = {
insert : function(text) { this.elem.innerHtml = text; return this;}
}
$('para1').insert('hello world').insert('chaining works too');
add any method u want to operate on elem in My_jquery.prototype
You can use a scheme like the following:
function $(id) {
return new DOMNode(id);
}
function DOMNode(id) {
this.element = document.getElementById(id);
}
DOMNode.prototype.insert = function(value) {
if (value) {
// If value is a string, assume its markup
if (typeof value == 'string') {
this.element.innerHTML = value;
// Otherwise assume it's an object
} else {
// If it's a DOM object
if (typeof value.nodeName == 'string') {
this.element.appendChild(value);
// If it's a DOMNode object
} else if (this.constructor == DOMNode) {
this.element.appendChild(value.element);
}
}
} // If all fails, do nothing
}
$('id').insert('foo bar');
Some play stuff:
<div id="d0">d0</div>
<div id="d1">d1</div>
<div id="d2">d2</div>
<script>
// insert (replace content with) string, may or may not be HTML
$('d0').insert('<b>foo bar</b>');
// insert DOMNode object
$('d0').insert($('d1'));
// Insert DOM element
$('d0').insert(document.getElementById('d2'));
</script>
You may find it useful to study how MyLibrary works, it has some very good practices and patterns.
Try this.
var getDOM= function(id) {
this.element= document.getElementById(id);
}
getDOM.prototype.insert= function(content) {
this.element.innerHTML= content;
}
var $= function(id) {
return new getDOM(id);
};
$('id').insert('Hello World!'); // can now insert 'Hello World!' into document.getElementById('id')

How to create a jQuery plugin with methods?

I'm trying to write a jQuery plugin that will provide additional functions/methods to the object that calls it. All the tutorials I read online (have been browsing for the past 2 hours) include, at the most, how to add options, but not additional functions.
Here's what I am looking to do:
//format div to be a message container by calling the plugin for that div
$("#mydiv").messagePlugin();
$("#mydiv").messagePlugin().saySomething("hello");
or something along those lines.
Here's what it boils down to: I call the plugin, then I call a function associated with that plugin. I can't seem to find a way to do this, and I've seen many plugins do it before.
Here's what I have so far for the plugin:
jQuery.fn.messagePlugin = function() {
return this.each(function(){
alert(this);
});
//i tried to do this, but it does not seem to work
jQuery.fn.messagePlugin.saySomething = function(message){
$(this).html(message);
}
};
How can I achieve something like that?
Thank you!
Update Nov 18, 2013: I've changed the correct answer to that of Hari's following comments and upvotes.
According to the jQuery Plugin Authoring page (http://docs.jquery.com/Plugins/Authoring), it's best not to muddy up the jQuery and jQuery.fn namespaces. They suggest this method:
(function( $ ){
var methods = {
init : function(options) {
},
show : function( ) { },// IS
hide : function( ) { },// GOOD
update : function( content ) { }// !!!
};
$.fn.tooltip = function(methodOrOptions) {
if ( methods[methodOrOptions] ) {
return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
// Default to "init"
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tooltip' );
}
};
})( jQuery );
Basically you store your functions in an array (scoped to the wrapping function) and check for an entry if the parameter passed is a string, reverting to a default method ("init" here) if the parameter is an object (or null).
Then you can call the methods like so...
$('div').tooltip(); // calls the init method
$('div').tooltip({ // calls the init method
foo : 'bar'
});
$('div').tooltip('hide'); // calls the hide method
$('div').tooltip('update', 'This is the new tooltip content!'); // calls the update method
Javascripts "arguments" variable is an array of all the arguments passed so it works with arbitrary lengths of function parameters.
Here's the pattern I have used for creating plugins with additional methods. You would use it like:
$('selector').myplugin( { key: 'value' } );
or, to invoke a method directly,
$('selector').myplugin( 'mymethod1', 'argument' );
Example:
;(function($) {
$.fn.extend({
myplugin: function(options,arg) {
if (options && typeof(options) == 'object') {
options = $.extend( {}, $.myplugin.defaults, options );
}
// this creates a plugin for each element in
// the selector or runs the function once per
// selector. To have it do so for just the
// first element (once), return false after
// creating the plugin to stop the each iteration
this.each(function() {
new $.myplugin(this, options, arg );
});
return;
}
});
$.myplugin = function( elem, options, arg ) {
if (options && typeof(options) == 'string') {
if (options == 'mymethod1') {
myplugin_method1( arg );
}
else if (options == 'mymethod2') {
myplugin_method2( arg );
}
return;
}
...normal plugin actions...
function myplugin_method1(arg)
{
...do method1 with this and arg
}
function myplugin_method2(arg)
{
...do method2 with this and arg
}
};
$.myplugin.defaults = {
...
};
})(jQuery);
What about this approach:
jQuery.fn.messagePlugin = function(){
var selectedObjects = this;
return {
saySomething : function(message){
$(selectedObjects).each(function(){
$(this).html(message);
});
return selectedObjects; // Preserve the jQuery chainability
},
anotherAction : function(){
//...
return selectedObjects;
}
};
}
// Usage:
$('p').messagePlugin().saySomething('I am a Paragraph').css('color', 'red');
The selected objects are stored in the messagePlugin closure, and that function returns an object that contains the functions associated with the plugin, the in each function you can perform the desired actions to the currently selected objects.
You can test and play with the code here.
Edit: Updated code to preserve the power of the jQuery chainability.
The problem with the currently selected answer is that you're not actually creating a new instance of the custom plugin for every element in the selector like you think you're doing... you're actually only creating a single instance and passing in the selector itself as the scope.
View this fiddle for a deeper explanation.
Instead, you'll need to loop through the selector using jQuery.each and instantiate a new instance of the custom plugin for every element in the selector.
Here's how:
(function($) {
var CustomPlugin = function($el, options) {
this._defaults = {
randomizer: Math.random()
};
this._options = $.extend(true, {}, this._defaults, options);
this.options = function(options) {
return (options) ?
$.extend(true, this._options, options) :
this._options;
};
this.move = function() {
$el.css('margin-left', this._options.randomizer * 100);
};
};
$.fn.customPlugin = function(methodOrOptions) {
var method = (typeof methodOrOptions === 'string') ? methodOrOptions : undefined;
if (method) {
var customPlugins = [];
function getCustomPlugin() {
var $el = $(this);
var customPlugin = $el.data('customPlugin');
customPlugins.push(customPlugin);
}
this.each(getCustomPlugin);
var args = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : undefined;
var results = [];
function applyMethod(index) {
var customPlugin = customPlugins[index];
if (!customPlugin) {
console.warn('$.customPlugin not instantiated yet');
console.info(this);
results.push(undefined);
return;
}
if (typeof customPlugin[method] === 'function') {
var result = customPlugin[method].apply(customPlugin, args);
results.push(result);
} else {
console.warn('Method \'' + method + '\' not defined in $.customPlugin');
}
}
this.each(applyMethod);
return (results.length > 1) ? results : results[0];
} else {
var options = (typeof methodOrOptions === 'object') ? methodOrOptions : undefined;
function init() {
var $el = $(this);
var customPlugin = new CustomPlugin($el, options);
$el.data('customPlugin', customPlugin);
}
return this.each(init);
}
};
})(jQuery);
And a working fiddle.
You'll notice how in the first fiddle, all divs are always moved to the right the exact same number of pixels. That is because only one options object exists for all elements in the selector.
Using the technique written above, you'll notice that in the second fiddle, each div is not aligned and is randomly moved (excluding the first div as it's randomizer is always set to 1 on line 89). That is because we are now properly instantiating a new custom plugin instance for every element in the selector. Every element has its own options object and is not saved in the selector, but in the instance of the custom plugin itself.
This means that you'll be able to access the methods of the custom plugin instantiated on a specific element in the DOM from new jQuery selectors and aren't forced to cache them, as you would be in the first fiddle.
For example, this would return an array of all options objects using the technique in the second fiddle. It would return undefined in the first.
$('div').customPlugin();
$('div').customPlugin('options'); // would return an array of all options objects
This is how you would have to access the options object in the first fiddle, and would only return a single object, not an array of them:
var divs = $('div').customPlugin();
divs.customPlugin('options'); // would return a single options object
$('div').customPlugin('options');
// would return undefined, since it's not a cached selector
I'd suggest using the technique above, not the one from the currently selected answer.
Use the jQuery UI Widget Factory.
Writing Stateful Plugins with the jQuery UI Widget Factory
How To Use the Widget Factory
Example:
$.widget( "myNamespace.myPlugin", {
options: {
// Default options
},
_create: function() {
// Initialization logic here
},
// Create a public method.
myPublicMethod: function( argument ) {
// ...
},
// Create a private method.
_myPrivateMethod: function( argument ) {
// ...
}
});
Initialization:
$('#my-element').myPlugin();
$('#my-element').myPlugin( {defaultValue:10} );
Method calling:
$('#my-element').myPlugin('myPublicMethod', 20);
(This is how the jQuery UI library is built.)
A simpler approach is to use nested functions. Then you can chain them in an object-oriented fashion. Example:
jQuery.fn.MyPlugin = function()
{
var _this = this;
var a = 1;
jQuery.fn.MyPlugin.DoSomething = function()
{
var b = a;
var c = 2;
jQuery.fn.MyPlugin.DoSomething.DoEvenMore = function()
{
var d = a;
var e = c;
var f = 3;
return _this;
};
return _this;
};
return this;
};
And here's how to call it:
var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();
Be careful though. You cannot call a nested function until it has been created. So you cannot do this:
var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();
pluginContainer.MyPlugin.DoSomething();
The DoEvenMore function doesn't even exist because the DoSomething function hasn't been run yet which is required to create the DoEvenMore function. For most jQuery plugins, you really are only going to have one level of nested functions and not two as I've shown here.
Just make sure that when you create nested functions that you define these functions at the beginning of their parent function before any other code in the parent function gets executed.
Finally, note that the "this" member is stored in a variable called "_this". For nested functions, you should return "_this" if you need a reference to the instance in the calling client. You cannot just return "this" in the nested function because that will return a reference to the function and not the jQuery instance. Returning a jQuery reference allows you to chain intrinsic jQuery methods on return.
I got it from jQuery Plugin Boilerplate
Also described in jQuery Plugin Boilerplate, reprise
// jQuery Plugin Boilerplate
// A boilerplate for jumpstarting jQuery plugins development
// version 1.1, May 14th, 2011
// by Stefan Gabos
// remember to change every instance of "pluginName" to the name of your plugin!
(function($) {
// here we go!
$.pluginName = function(element, options) {
// plugin's default options
// this is private property and is accessible only from inside the plugin
var defaults = {
foo: 'bar',
// if your plugin is event-driven, you may provide callback capabilities
// for its events. execute these functions before or after events of your
// plugin, so that users may customize those particular events without
// changing the plugin's code
onFoo: function() {}
}
// to avoid confusions, use "plugin" to reference the
// current instance of the object
var plugin = this;
// this will hold the merged default, and user-provided options
// plugin's properties will be available through this object like:
// plugin.settings.propertyName from inside the plugin or
// element.data('pluginName').settings.propertyName from outside the plugin,
// where "element" is the element the plugin is attached to;
plugin.settings = {}
var $element = $(element), // reference to the jQuery version of DOM element
element = element; // reference to the actual DOM element
// the "constructor" method that gets called when the object is created
plugin.init = function() {
// the plugin's final properties are the merged default and
// user-provided options (if any)
plugin.settings = $.extend({}, defaults, options);
// code goes here
}
// public methods
// these methods can be called like:
// plugin.methodName(arg1, arg2, ... argn) from inside the plugin or
// element.data('pluginName').publicMethod(arg1, arg2, ... argn) from outside
// the plugin, where "element" is the element the plugin is attached to;
// a public method. for demonstration purposes only - remove it!
plugin.foo_public_method = function() {
// code goes here
}
// private methods
// these methods can be called only from inside the plugin like:
// methodName(arg1, arg2, ... argn)
// a private method. for demonstration purposes only - remove it!
var foo_private_method = function() {
// code goes here
}
// fire up the plugin!
// call the "constructor" method
plugin.init();
}
// add the plugin to the jQuery.fn object
$.fn.pluginName = function(options) {
// iterate through the DOM elements we are attaching the plugin to
return this.each(function() {
// if plugin has not already been attached to the element
if (undefined == $(this).data('pluginName')) {
// create a new instance of the plugin
// pass the DOM element and the user-provided options as arguments
var plugin = new $.pluginName(this, options);
// in the jQuery version of the element
// store a reference to the plugin object
// you can later access the plugin and its methods and properties like
// element.data('pluginName').publicMethod(arg1, arg2, ... argn) or
// element.data('pluginName').settings.propertyName
$(this).data('pluginName', plugin);
}
});
}
})(jQuery);
Too late but maybe it can help someone one day.
I was in the same situation like, creating a jQuery plugin with some methods, and after reading some articles and some tires I create a jQuery plugin boilerplate (https://github.com/acanimal/jQuery-Plugin-Boilerplate).
In addition, I develop with it a plugin to manage tags (https://github.com/acanimal/tagger.js) and wrote a two blog posts explaining step by step the creation of a jQuery plugin (https://www.acuriousanimal.com/blog/20130115/things-i-learned-creating-a-jquery-plugin-part-i).
You can do:
(function($) {
var YourPlugin = function(element, option) {
var defaults = {
//default value
}
this.option = $.extend({}, defaults, option);
this.$element = $(element);
this.init();
}
YourPlugin.prototype = {
init: function() { },
show: function() { },
//another functions
}
$.fn.yourPlugin = function(option) {
var arg = arguments,
options = typeof option == 'object' && option;;
return this.each(function() {
var $this = $(this),
data = $this.data('yourPlugin');
if (!data) $this.data('yourPlugin', (data = new YourPlugin(this, options)));
if (typeof option === 'string') {
if (arg.length > 1) {
data[option].apply(data, Array.prototype.slice.call(arg, 1));
} else {
data[option]();
}
}
});
};
});
In this way your plugins object is stored as data value in your element.
//Initialization without option
$('#myId').yourPlugin();
//Initialization with option
$('#myId').yourPlugin({
// your option
});
// call show method
$('#myId').yourPlugin('show');
What about using triggers? Does anyone know any drawback using them?
The benefit is that all internal variables are accessible via the triggers, and the code is very simple.
See on jsfiddle.
Example usage
<div id="mydiv">This is the message container...</div>
<script>
var mp = $("#mydiv").messagePlugin();
// the plugin returns the element it is called on
mp.trigger("messagePlugin.saySomething", "hello");
// so defining the mp variable is not needed...
$("#mydiv").trigger("messagePlugin.repeatLastMessage");
</script>
Plugin
jQuery.fn.messagePlugin = function() {
return this.each(function() {
var lastmessage,
$this = $(this);
$this.on('messagePlugin.saySomething', function(e, message) {
lastmessage = message;
saySomething(message);
});
$this.on('messagePlugin.repeatLastMessage', function(e) {
repeatLastMessage();
});
function saySomething(message) {
$this.html("<p>" + message + "</p>");
}
function repeatLastMessage() {
$this.append('<p>Last message was: ' + lastmessage + '</p>');
}
});
}
Here I want to suggest steps to create simple plugin with arguments.
(function($) {
$.fn.myFirstPlugin = function(options) {
// Default params
var params = $.extend({
text : 'Default Title',
fontsize : 10,
}, options);
return $(this).text(params.text);
}
}(jQuery));
$('.cls-title').myFirstPlugin({ text : 'Argument Title' });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 class="cls-title"></h1>
Here, we have added default object called params and set default values of options using extend function. Hence, If we pass blank argument then it will set default values instead otherwise it will set.
Read more: How to Create JQuery plugin
Try this one:
$.fn.extend({
"calendar":function(){
console.log(this);
var methods = {
"add":function(){console.log("add"); return this;},
"init":function(){console.log("init"); return this;},
"sample":function(){console.log("sample"); return this;}
};
methods.init(); // you can call any method inside
return methods;
}});
$.fn.calendar() // caller or
$.fn.calendar().sample().add().sample() ......; // call methods
Here is my bare-bones version of this. Similar to the ones posted before, you would call like:
$('#myDiv').MessagePlugin({ yourSettings: 'here' })
.MessagePlugin('saySomething','Hello World!');
-or access the instance directly # plugin_MessagePlugin
$elem = $('#myDiv').MessagePlugin();
var instance = $elem.data('plugin_MessagePlugin');
instance.saySomething('Hello World!');
MessagePlugin.js
;(function($){
function MessagePlugin(element,settings){ // The Plugin
this.$elem = element;
this._settings = settings;
this.settings = $.extend(this._default,settings);
}
MessagePlugin.prototype = { // The Plugin prototype
_default: {
message: 'Generic message'
},
initialize: function(){},
saySomething: function(message){
message = message || this._default.message;
return this.$elem.html(message);
}
};
$.fn.MessagePlugin = function(settings){ // The Plugin call
var instance = this.data('plugin_MessagePlugin'); // Get instance
if(instance===undefined){ // Do instantiate if undefined
settings = settings || {};
this.data('plugin_MessagePlugin',new MessagePlugin(this,settings));
return this;
}
if($.isFunction(MessagePlugin.prototype[settings])){ // Call method if argument is name of method
var args = Array.prototype.slice.call(arguments); // Get the arguments as Array
args.shift(); // Remove first argument (name of method)
return MessagePlugin.prototype[settings].apply(instance, args); // Call the method
}
// Do error handling
return this;
}
})(jQuery);
The following plugin-structure utilizes the jQuery-data()-method to provide a public interface to internal plugin-methods/-settings (while preserving jQuery-chainability):
(function($, window, undefined) {
const defaults = {
elementId : null,
shape : "square",
color : "aqua",
borderWidth : "10px",
borderColor : "DarkGray"
};
$.fn.myPlugin = function(options) {
// settings, e.g.:
var settings = $.extend({}, defaults, options);
// private methods, e.g.:
var setBorder = function(color, width) {
settings.borderColor = color;
settings.borderWidth = width;
drawShape();
};
var drawShape = function() {
$('#' + settings.elementId).attr('class', settings.shape + " " + "center");
$('#' + settings.elementId).css({
'background-color': settings.color,
'border': settings.borderWidth + ' solid ' + settings.borderColor
});
$('#' + settings.elementId).html(settings.color + " " + settings.shape);
};
return this.each(function() { // jQuery chainability
// set stuff on ini, e.g.:
settings.elementId = $(this).attr('id');
drawShape();
// PUBLIC INTERFACE
// gives us stuff like:
//
// $("#...").data('myPlugin').myPublicPluginMethod();
//
var myPlugin = {
element: $(this),
// access private plugin methods, e.g.:
setBorder: function(color, width) {
setBorder(color, width);
return this.element; // To ensure jQuery chainability
},
// access plugin settings, e.g.:
color: function() {
return settings.color;
},
// access setting "shape"
shape: function() {
return settings.shape;
},
// inspect settings
inspectSettings: function() {
msg = "inspecting settings for element '" + settings.elementId + "':";
msg += "\n--- shape: '" + settings.shape + "'";
msg += "\n--- color: '" + settings.color + "'";
msg += "\n--- border: '" + settings.borderWidth + ' solid ' + settings.borderColor + "'";
return msg;
},
// do stuff on element, e.g.:
change: function(shape, color) {
settings.shape = shape;
settings.color = color;
drawShape();
return this.element; // To ensure jQuery chainability
}
};
$(this).data("myPlugin", myPlugin);
}); // return this.each
}; // myPlugin
}(jQuery));
Now you can call internal plugin-methods to access or modify plugin data or the relevant element using this syntax:
$("#...").data('myPlugin').myPublicPluginMethod();
As long as you return the current element (this) from inside your implementation of myPublicPluginMethod() jQuery-chainability
will be preserved - so the following works:
$("#...").data('myPlugin').myPublicPluginMethod().css("color", "red").html("....");
Here are some examples (for details checkout this fiddle):
// initialize plugin on elements, e.g.:
$("#shape1").myPlugin({shape: 'square', color: 'blue', borderColor: 'SteelBlue'});
$("#shape2").myPlugin({shape: 'rectangle', color: 'red', borderColor: '#ff4d4d'});
$("#shape3").myPlugin({shape: 'circle', color: 'green', borderColor: 'LimeGreen'});
// calling plugin methods to read element specific plugin settings:
console.log($("#shape1").data('myPlugin').inspectSettings());
console.log($("#shape2").data('myPlugin').inspectSettings());
console.log($("#shape3").data('myPlugin').inspectSettings());
// calling plugin methods to modify elements, e.g.:
// (OMG! And they are chainable too!)
$("#shape1").data('myPlugin').change("circle", "green").fadeOut(2000).fadeIn(2000);
$("#shape1").data('myPlugin').setBorder('LimeGreen', '30px');
$("#shape2").data('myPlugin').change("rectangle", "red");
$("#shape2").data('myPlugin').setBorder('#ff4d4d', '40px').css({
'width': '350px',
'font-size': '2em'
}).slideUp(2000).slideDown(2000);
$("#shape3").data('myPlugin').change("square", "blue").fadeOut(2000).fadeIn(2000);
$("#shape3").data('myPlugin').setBorder('SteelBlue', '30px');
// etc. ...
This can actually be made to work in a "nice" way using defineProperty. Where "nice" means without having to use () to get plugin namespace nor having to pass function name by string.
Compatibility nit: defineProperty doesn't work in ancient browsers such as IE8 and below.
Caveat: $.fn.color.blue.apply(foo, args) won't work, you need to use foo.color.blue.apply(foo, args).
function $_color(color)
{
return this.css('color', color);
}
function $_color_blue()
{
return this.css('color', 'blue');
}
Object.defineProperty($.fn, 'color',
{
enumerable: true,
get: function()
{
var self = this;
var ret = function() { return $_color.apply(self, arguments); }
ret.blue = function() { return $_color_blue.apply(self, arguments); }
return ret;
}
});
$('#foo').color('#f00');
$('#bar').color.blue();
JSFiddle link
According to jquery standard you can create plugin as follow:
(function($) {
//methods starts here....
var methods = {
init : function(method,options) {
this.loadKeywords.settings = $.extend({}, this.loadKeywords.defaults, options);
methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
$loadkeywordbase=$(this);
},
show : function() {
//your code here.................
},
getData : function() {
//your code here.................
}
} // do not put semi colon here otherwise it will not work in ie7
//end of methods
//main plugin function starts here...
$.fn.loadKeywords = function(options,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 ecw-Keywords');
}
};
$.fn.loadKeywords.defaults = {
keyName: 'Messages',
Options: '1',
callback: '',
};
$.fn.loadKeywords.settings = {};
//end of plugin keyword function.
})(jQuery);
How to call this plugin?
1.$('your element').loadKeywords('show',{'callback':callbackdata,'keyName':'myKey'}); // show() will be called
Reference: link
I think this might help you...
(function ( $ ) {
$.fn.highlight = function( options ) {
// This is the easiest way to have default options.
var settings = $.extend({
// These are the defaults.
color: "#000",
backgroundColor: "yellow"
}, options );
// Highlight the collection based on the settings variable.
return this.css({
color: settings.color,
backgroundColor: settings.backgroundColor
});
};
}( jQuery ));
In the above example i had created a simple jquery highlight plugin.I had shared an article in which i had discussed about How to Create Your Own jQuery Plugin from Basic to Advance.
I think you should check it out... http://mycodingtricks.com/jquery/how-to-create-your-own-jquery-plugin/
Following is a small plug-in to have warning method for debugging purpose. Keep this code in jquery.debug.js file:
JS:
jQuery.fn.warning = function() {
return this.each(function() {
alert('Tag Name:"' + $(this).prop("tagName") + '".');
});
};
HTML:
<html>
<head>
<title>The jQuery Example</title>
<script type = "text/javascript"
src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src = "jquery.debug.js" type = "text/javascript"></script>
<script type = "text/javascript" language = "javascript">
$(document).ready(function() {
$("div").warning();
$("p").warning();
});
</script>
</head>
<body>
<p>This is paragraph</p>
<div>This is division</div>
</body>
</html>
Here is how I do it:
(function ( $ ) {
$.fn.gridview = function( options ) {
..........
..........
var factory = new htmlFactory();
factory.header(...);
........
};
}( jQuery ));
var htmlFactory = function(){
//header
this.header = function(object){
console.log(object);
}
}
What you did is basically extending jQuery.fn.messagePlugin object by new method. Which is useful but not in your case.
You have to do is using this technique
function methodA(args){ this // refers to object... }
function saySomething(message){ this.html(message); to first function }
jQuery.fn.messagePlugin = function(opts) {
if(opts=='methodA') methodA.call(this);
if(opts=='saySomething') saySomething.call(this, arguments[0]); // arguments is an array of passed parameters
return this.each(function(){
alert(this);
});
};
But you can accomplish what you want I mean there is a way to do $("#mydiv").messagePlugin().saySomething("hello"); My friend he started writing about lugins and how to extend them with your chainf of functionalities here is the link to his blog

Categories

Resources