If I define prototype like this
jQuery.fn.myFunc = function() {
console.log(this);
}
and call it like this:
$('div').myFunc();
this inside of myFunc will refer to jQuery object selection.
Now if I don't want to pollute .fn with multiple attached functions, can I do something like
jQuery.fn.myPlugin = {
myFunc1: function() {
},
myFunc2: function() {
}
}
If I call it $('div').myPlugin.myFunc1(); - how do I get reference to selected objects inside of myFunc1? Or is there a different, better approach?
Nope. can't do it that way. Instead, define it as a function, then add properties to that function.
jQuery.fn.myPlugin = function() {
console.log(this);
};
jQuery.fn.myPlugin.myFunc1 = function() {
};
jQuery.fn.myPlugin.myFunc2 = function() {
};
note that myFunc1 and myFunc2 still wont' have access to the selected element, therefore it's relatively useless to define them this way other than the fact that they can be easily overridden by other developers (which is a good thing)
The normal way of having additional methods within your plugin is to have your plugin method accept a parameter that can eitehr be an object to init the plugin, or a string to execute a target method on the element. for example, $("somediv").myPlugin("myFunc1")
The jQuery plugin tutorial suggests this:
(function( $ ) {
$.fn.popup = function( action ) {
if ( action === "open") {
// Open popup code.
}
if ( action === "close" ) {
// Close popup code.
}
};
}( jQuery ));
I suppose this would be another alternative:
(function($) {
$.fn.myPlugin = function (action) {
var functions = {
open: function () {
console.log('open: ', this);
},
close: function () {
console.log('close:', this);
}
}
if (action && functions[action]) {
functions[action].call(this)
} else {
console.log('no function', this);
}
return this;
};
}(jQuery));
$('#theDiv')
.myPlugin()
.myPlugin('open')
.myPlugin('close');
http://jsfiddle.net/faJAk/
work if you create a object before.
Like this:
<script>
jQuery.fn.myPlugin = {};
jQuery.fn.myPlugin = {
myFunc1: function() {
console.log(this);
},
myFunc2: function() {
alert(this);
}
};
$(document).ready(function(){
$('div').myPlugin.myFunc1();
$('div').myPlugin.myFunc2();
});
</script>
Another possible approach is to use defineProperty:
(function($) {
var myPlugin = {
foo: function() {
console.log(this)
}
}
Object.defineProperty($.fn, "myPlugin", {
get : function() { return $.extend({}, this, myPlugin) }
});
})(jQuery);
Now $(...).myPlugin.foo should resolve this correctly:
$(function() {
$("body").myPlugin.foo(); // logs "body"
})
Related
I am moving some jquery functions into a javascript object to clean up some code. My problem is, when I put methods on my object's constructor, calling this.functionName() returns the error this.functionName is not a function but if my functions are helper methods and are outside of the object's constructor, they work just fine.
Here is my code that does not work
function MyConstructor() {
this.init();
this.selectAllHandler();
}
MyConstructor.prototype = {
init: function() {
var self = this;
$(document).on('click', '#my_element', function() {
self.selectAllHandler.call(this);
});
},
selectAllHandler: function() {
// handler works fine
var ids_array = this.idsArray(checkboxes); // error happening here
},
// helpers
idsArray: function(checkboxes) {
// trying to call
}
}
But, having my object w/ a constructor and then calling the "helper" outside of the object works fine. For example, this works fine.
function MyConstructor() {
this.init();
}
MyConstructor.prototype = {
init: function() {
var self = this;
$(document).on('click', '#my_element', function() {
self.selectAllHandler.call(this);
});
},
selectAllHandler: function() {
// handler works fine
var ids_array = idsArray(checkboxes);
}
}
function idsArray() {
// code that works fine
}
One thing to note as well, is that in this scenario, by running console.log this refers to the element being clicked on, and not the constructor.
I have tried using call, apply, and bind, but have not had success, though I think it's been syntax related.
How can I build this so I can call my "helper" functions inside my object?
Not sure how you were using bind, since you said it didn't work for you.
If you want, you can use bind like below. Also, in your code snippet checkboxes was not defined. This way you don't need to use self.
function MyConstructor() {
this.init();
this.selectAllHandler();
}
MyConstructor.prototype = {
init: function() {
//var self = this;
$(document).on('click', '#my_element', function() {
//self.selectAllHandler.call(self);
this.selectAllHandler();
}.bind(this));
},
selectAllHandler: function() {
// handler works fine
var checkboxes;
var ids_array = this.idsArray(checkboxes); // error happening here
},
// helpers
idsArray: function(checkboxes) {
// trying to call
console.log('test');
}
}
var o = new MyConstructor();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I was able to figure it out. I thought I could call another function in the constructor just using this.functionName(). however, $(this) was referring to the element I was clicking on.
I remembered I defined self (this) in my init function which refers to the window object. Well, inside the window object is my object, and my function is on that object. So i was able to successfully call my object by doing
function MyConstructor() {
this.init();
}
MyConstructor.prototype = {
init: function() {
var self = this;
$(document).on('click', '#my_element', function() {
self.selectAllHandler.call(this);
});
},
selectAllHandler: function() {
// RIGHT HERE
var ids_array = self.MyConstructor.prototype.idsArray(checkboxes);
},
// helpers
idsArray: function(checkboxes) {
// some codes
}
}
I have a really annoying scope issue for my JQuery Widget. Essentially I need to access the widget instance (this) inside of my map/object options.
Is it possible to do this? Any advice how I can achieve this?
$.widget( "my.myWidget", {
// Below 'this.defCallback' will be undefined
// How can I store 'this' (the widget instance) in a variable??
options: {
callback: this.defCallback // allow user to overwrite/provide custom callback
},
....
defCallback: function() {
console.log('defCallback');
}
});
If I had a nested function I know I can easily solve this but I have a nested object/map which makes things difficult.
function foo {
var _this = this;
...
var bar = function() {
// easily access this
_this.defCallback();
...
}
}
Usage:
$('<div></div>')
.myWidget(); // use defCallback
$('<div></div>')
.myWidget({
callback: function() {
...
}
}); // use custom callback
Edit: How the callback function is 'bound' and called:
_create: function() {
this.element.click( this.options.callback );
}
.click(value.callback(_this)
In javascript you could dynamically change the context of a function with the apply() and with call() methods.
On es5 you could use bind().
So your code:
_create: function() {
this.element.click( this.options.callback );
}
Became with apply():
_create: function() {
var el = this.element;
var callback = this.options.callback;
el.click(function() {
callback.apply(el);
// If you have parameters:
// callback.apply(el, arguments || array);
});
}
With call():
_create: function() {
var el = this.element;
var callback = this.options.callback;
el.click(function() {
callback.call(el);
// If you have parameters:
// callback.call(el, arg0, arg1, ...);
});
}
With bind():
_create: function() {
this.element.click(this.options.callback.bind(this));
}
UPDATE
As your issue is to have the this reference binded inside the object definition you need to change your code.
The quick way is is to emend it like this (from your fiddle):
var mw = {
defCallback: function () {
alert("abc");
},
_create: function () {
//this.populateOptions();
alert("Is undefined: " + this.options.isUndefined); // outputs 'true'
this.element.click(this.options.callback.bind(this));
},
populateOptions: function() {
if (this.options.callback === undefined)
this.options.callback = this.defCallback;
}
};
So you first define your object with the parent attributes and functions.
mw.options = {
//accessObjectParent: this.instantiator,
isUndefined: (mw.defCallback === undefined), // this refers to the map/object
// Can I access the maps 'parent'/instantiator?
// this.instantiator.defCallback ???
callback: mw.defCallback
};
Than you attach the options object and you could refer on the parent object instead of using this.
$.widget( "my.myWidget", mw );
And now you pass the object on your widget declaration.
Let’s say I have following simple plugin:
(function ( $ ) {
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
}( jQuery ));
I don’t want to make any changes to the plugin itself, but I would like to wrap with another function and extend its namespace as follow:
(function($) {
$.fn.myFunction = function () {
(function ($) {
$.fn.greenify = function () {
this.css("color", "green");
return this;
};
}(jQuery));
};
})(jQuery);
So I can call the plugin function like this:
$(selector). myFunction().greenify();
Basically I want to disable calling ‘greenify’ function directly.
Is it possible?
It's not clear from the question, but I'm assuming the simple plugin "greenify" is a third-party or other "forced to use" plugin that you, for whatever reason, can't change. Let's also assume that it's actually quite a complicated plugin and simplified for the sake of the question.
This means
you can't change it
you can't duplicate the entire plugin inside your wrapper
The usual method for overwriting something is to take a copy, then make the new version do what you want, possibly calling the old version, eg:
var oldfoo = foo;
foo = function() {
alert("foo called");
oldfoo(); // or oldfoo.apply(this) to be clearer
}
The same principle can be applied here, but instead make 'foo' (in the example above) null - to get rid of it, eg:
var oldfoo = foo;
newfoo = function() {
alert("foo called");
oldfoo(); // or oldfoo.apply(this) to be clearer
}
foo = null;
The complication is with jquery and wanting to keep the method chaining, which can be achieved by storing 'this' and applying it as desired.
Here's the full code with explanation comments:
// The original plugin to be wrapped
(function ( $ ) {
$.fn.greenify = function() {
// changed to background-color for more impact (demo purposes)
this.css( "background-color", "lightgreen" );
return this;
};
}( jQuery ));
(function($) {
// allow this to be referred to later
// inside another loop where 'this' is something else
var me = this;
// take a copy of the original
// this stays inside the scope of (function($)) so can't be seen outside
me.original_greeny = $.fn.greenify;
// provide a wrapper
$.fn.myFunction = function () {
// the jquery elements for applying later
var $this = $(this)
// exported function
return {
greenify: function() {
// Call the original function with the jquery elements
// and return them for chaining
return me.original_greeny.apply($this)
}
};
};
})(jQuery);
// Now remove the original completely
(function ( $ ) {
$.fn.greenify = null;
}(jQuery));
// As desired, also demonstrating chaining still works
$("#a").myFunction().greenify().css("font-style", "italic")
// Confirm that the original has been removed
// gives error $(...).greenify is not a function
try {
$("#a").greenify()
} catch(e) {
$("#a").text("error on $().greenify: " + e)
}
and a jsfiddle
If you want to create your own context, one way is to return an object with a set of functions:
(function($) {
$.fn.myFunction = function () {
// Cache jQuery object
var $this = this;
// Return interface that acts on jQuery object
return {
greenify: function () {
$this.css("color", "green");
return $this;
}
};
};
})(jQuery);
Then you could call it using:
$(selector).myFunction().greenify();
Fiddle
Edit: As a warning though, when you do this you are leaving the jQuery chaining context after calling .myFunction, which can be very confusing in code.
You definitely don't want to take the current shown approach, because every time myFunction is called, it will also assign greenify. Simply take your myFunction plugin, assign a flag to the jQuery object that was constructed, and then check for that flag in greenify.
(function($) {
$.fn.myFunction = function () {
this.greenMarker = true;
return this;
};
$.fn.greenify = function () {
if(!this.greenMarker) return this;
this.css("color", "green");
return this;
};
})(jQuery);
$('.f').greenify();
$('.g').myFunction().greenify();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="f">No Green</div>
<div class="g">Green Me</div>
I think you're talking about method chaining which is the beauty of jQuery methods. Define your new method as follows, but leave greenify() unchanged:
(function($) {
$.fn.myFunction = function () {
return this.each(function() {
//Do stuff
});
};
})(jQuery);
(function ( $ ) {
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
}( jQuery ));
(function($) {
$.fn.myFunction = function () {
return this.each(function() {
$(this).css({border:'1px solid black',textAlign:'center'});
});
};
})(jQuery);
$('.box').myFunction().greenify();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box">COLOR CHANGE</div>
Pretty sure so long as your myFunction returns this, that you should be able to chain whatever you want to it.
(function ( $ ) {
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
}( jQuery ));
(function($) {
$.fn.myFunction = function () {
// Do some stuff
return this;
};
})(jQuery);
$(selector).myFunction().greenify();
EDIT: If the whole point is to "disable calling greenify directly", why would you extend jQuery in the first place? 'greenify could be nested and triggered by passing an argument of some kind. Something like:
(function($) {
$.fn.myFunction = function (options) {
// Do some stuff
if (options.greenify) {
this.css("color", "green");
}
return this;
};
})(jQuery);
$(selector).myFunction({greenify: true});
... or you could just define greenify as a function (instead of a plugin) and call it. But the point of defining plugins is so they can then be called globally.
I have a code like this:
var methods = {
collapse: function(element) {
modify(element);
},
other_method: function() {
// ...
}
};
function modify(element)
{
console.log('collapse method');
}
Is it possible to minify collapse method to one line? So it should always call modify function.
Try this:
var methods = {
collapse: modify,
other_method: function() {
// ...
}
};
function modify(element) {
console.log('collapse method');
}
Because we have function declaration (not expression), modify is visible when you declare the object methods. The thing which is done here is just setting collapse to be equal to modify's reference.
This is the same as:
var modify = function (element) {
console.log('collapse method');
}
var methods = {
other_method: function() {
// ...
}
};
methods.collapse = modify;
(function( $ ){
$.fn.foo = function(params) {
params = $.extend( {
on: false
}, params);
this.each(function(){
if(params.on) {
function alertDate(){
alert('FOO BAR!!!');
}
}
});
};
})( jQuery );
How can i access the "alertDate()" function from out of the script?
if i use:
$('#test').foo()
will give-me access for the function, ok, everything fine, i will gain access to function "alertDate()" internally at this.each(function(){}).
I want to access the function "alertDate()" externally by something like:
$('#text').foo({on: 'true'}).each().alertDate();
How can i do this?
Thanks in advance and sorry for the poor english
Here's how I would handle it, so that your alertDate() function can be used both internally and externally:
$.fn.foo = function(params) {
params = $.extend( {
on: false
}, params);
this.alertDate = function(){
alert("FOO BAR!!!");
}
this.each(function(){
if(params.on) {
this.alertDate();
}
});
return this;
};
//Externally the alertDate function could be called as:
$("test").foo().alertDate();
You can do something like this, but this is not how plugins normally work.
$.fn.foo = function() {
function bar() {
alert("bar");
}
this.bar = bar;
return this;
};
$("a").foo().bar();