I'm beginning with jQuery plugins, apologies for the newbie question. My objective is to have a single plugin instantiated twice, where each instance has its own variables values. However, they seem to share the namespace.
For example, given the following plugin:
(function ( $ ) {
var x = false;
$.fn.test = function() {
alert(x);
if ( !x )
x = true;
return this;
};
}( jQuery ));
that is invoked from the following divs:
$( "div1" ).test();
$( "div2" ).test();
The alert displays first false, then true, when the objective is to have to sets of variables where the alert would display false twice.
is this possible?
There is some confusion in your question. Your plugin is a simple function. You don't "instantiate" a function by calling it. So you don't "instantiate" your plugin either.
You can instantiate things in your function, and persist them somewhere.
Since the outer scope runs only once, in your original solution you only get one instance of variable x.
If you create it inside the function, a new instance gets created every time you call it.
I assume you want to create an instance for every element you call this plugin on. A good solution would be to attach your data to the DOM element you initiate your plugin on, like:
(function ( $ ) {
$.fn.test = function() {
var vars = this.data('testPlugin');
if (!vars) {
vars = {
x: false,
something: 'else'
};
this.data('testPlugin', vars);
}
alert(vars.x);
vars.x = !vars.x;
return this;
};
}( jQuery ));
Try this fiddle.
You should put
var x = false;
inside $.fn.test function, otherwise the x variable is the same for all test() functions, and set to true after first call.
You can read more here about javascript variable scoping.
Actually, this is much easier than the previous answers. The context of this in your plugin is the jQuery object for the DOM element you're receiving based on the selector you provided. To gain uniqueness, simply iterate over each element, like so:
(function($) {
$.fn.test = function() {
return this.each(function() {
var x = false;
alert(x);
if (!x) {
x = true;
}
});
}
}(jQuery));
$("div1").test(); //false
$("div2").test(); // false
Here's a JSFiddle to confirm: http://jsfiddle.net/Z6j7f/
Related
I am bulding a plugin let's call it ptest and I want to be able to call it with:
$(".myClassName").ptest();
Since I am using attributes from the element on which the plugin is called, lets say data-attribute I now know that returning this.each(...); is a must.
Here is my code:
(function($){
var op;
$.fn.ptest = function(options) {
op = $.extend({
target: null,
attribute: null
}, options);
return this.each(function(){
op.target = $(this);
op.attribute = op.target.attr("data-attribute");
bind();
});
};
function bind(){
op.target.find('.clickable').bind('click',log);
}
function log(){
console.log(op.attribute);
}
}(jQuery));
I know that by having op as a global variable it will always retain the last value for the attribute and the target. How can I make the op variable retain the correct value for each element of .myClassName while being able to access each op from log or bind functions?
I sense i need to declare the functions and the variable in a different way, but how?
I have looked at a lot of different questions and tutorials, here are some:
http://devheart.org/articles/tutorial-creating-a-jquery-plugin/
jQuery plugin development - return this.each issue
jQuery Plugin Return this.each and add function property to each object?
https://learn.jquery.com/plugins/ (of course)
If bind and log really need access to the specific element in the loop, then you need to define them within the each callback, and make op local to that callback:
(function($){
$.fn.ptest = function(options) {
return this.each(function(){
var op = $.extend({
target: $(this)
}, options);
op.attribute = op.target.attr("data-attribute");
bind();
function bind(){
op.target.find('.clickable').bind('click',log);
}
function log(){
console.log(op.attribute);
}
});
};
}(jQuery));
But depending on how you're using bind and log, there may be other options available.
I can't figure out how to use a Javascript constructor method in a jQuery .click method. I'm trying to get a button's function to change dynamically based on a constructor. Here's the set up:
<button onclick="">
</button>
needs to call a method that changes depending on another button. The following is my broken code:
function GloveMode (name , array) {
this.colorArray = array;
this.displaySettings = function(){
//Title
$("#displayTitle").text(this.name);
//Display Color Set
$("#displayColors").empty();
//Totally Broken
$("#upArrow").click( function(){
addColor();
});
};
this.addColor = function(){
console.log(this.colorArray.length);
};
};
I can't figure out how to get $("#upArrow").click() to call this.colorArray properly, or how to call this.addColor() in the .click() method! Please help.
Your Problem is that "this" means something different in each function body. So save the wanted "this" to a variable e.g. "self" and use that.
function GloveMode (name , array)
{
var self = this;
this.colorArray = array;
this.displaySettings = function()
{
//Title
$("#displayTitle").text(this.name);
//Display Color Set
$("#displayColors").empty();
//Totally Broken
$("#upArrow").click( function()
{
self.addColor();
});
};
this.addColor = function()
{
console.log(self.colorArray.length);
};
};
I am new to jQuery and just learning new stuff. I was just reading through Chris Coyer's article and came across the following code :
$.fn.faq = function(options) {
return this.each(function(i, el) {
var base = el,
$base = $(el);
console.log(options);
base.init = function() {
// Do initialization stuff
$base
.find("dd")
.hide()
.end()
.find("dt")
.click(function() {
var ans = $(this).next();
if (ans.is(":visible")) {
base.closeQ(ans);
} else {
base.openQ(ans);
}
})
};
base.openQ = function(ans) {
// Open panel
ans.show();
// Do callback
options.qOpen.call();
};
base.closeQ = function(ans) {
// Open panel
ans.hide();
// Do callback
options.qClose.call();
};
base.init();
});
};
$("dl").faq({
qOpen: myQuestionOpenCallback,
qClose: myQuestionCloseCallback
});
function myQuestionOpenCallback() {
alert("answer opened!");
}
function myQuestionCloseCallback() {
alert("answer closed!");
}
Now I didn't quite understand this part of the code:
return this.each(function(i, el) {
The second line in the code, what exactly is i and el? I don't see anywhere these parameters being passed into the each function.
I asked a senior colleague of mine and got the following answer:
Many plugins start that way. Since most plugins are chainable, they
have to return this. And they also have to loop through the elements
from the selector,
return this.each(function(i, el) {
does them both. A loop, then the return.
but I still didn't quite understand.
The JS Fiddle can be found here.
Inside a jQuery plugin, this refers to the jQuery object representing what you called the plugin on. For example, in the case of this faq plugin, if I call $('#someDiv').faq({ ... });, this will be the same as $('#someDiv') inside the plugin function.
So because it is a jQuery object representing a selection of DOM nodes, you can call the jQuery method .each() on it, which takes a function that gets given two parameters when it is called for each DOM node in the selection:
The index (0, 1, 2 and so on)
The DOM node itself
.each() also returns the thing it was called on, so you end up returning the $('#someDiv') object from the plugin. That's great, because then we can call some other jQuery method on it straight afterwards ("chaining"). e.g. $('#someDiv').faq({ ... }).hide(); to hide it immediately.
https://api.jquery.com/jquery.each/
i : index of the element.
el : the DOM element (not a jQuery object).
I am trying to add a functionality to a web page that uses a jquery library which doesn't seem to have any documentation. (unknown origin) my problem is mainly due to the lack of understanding on jquery plugin model and/or inner workings of javascript.
1. the plugin is initiated as follows
jQuery('div.carousel').scrollGallery({
mask: 'div.mask',
slider: 'div.slideset',
slides: 'div.slide', ............ });
2. the plugin is defined in jquery as follows
;(function($){
function ScrollGallery(options) {
this.options = $.extend({
mask: 'div.mask', ...... }, options);
this.init();
3. in the Object.prototype declaration i see the following function numSlide defined.
ScrollGallery.prototype = {
....................
numSlide: function(c) {
if(this.currentStep != c) {
this.currentStep = c;
this.switchSlide();
}
},
.......... };
Question.
How do i reference numSlide(int) function externally?.
I tried the following methods and it did not work.
myx = jQuery('div.carousel').scrollGallery({ // var myx was added in the global scope
myx.numSlide(1); //error undefined is not a function
i tried adding return this; at the end of myx = jQuery('div.carousel').scrollGallery({ but it still returns the jQuery object.
i also tried
jQuery.scrollGallery().numSlide(2); //error undefined is not a function
jQuery.scrollGallery.numSlide(2); //same error
Do i need to add LIGHT BULB
// jquery plugin
$.fn.scrollGallery = function(opt){
return this.each(function(){
$(this).data('ScrollGallery', new ScrollGallery($.extend(opt,{holder:this})));
});
};
}(jQuery));
ANSWER (I think)
it looks like the ScrollGalary object is stored in a data for the selector. So i believe i can do the following jQuery('selector').data('ScrollGallery').numSlide(2);
I decided to post this anyway in-case if anyone in the future had a similar gullible situation.
One way of doing this will be to initiate ScrollGallery object first and then use it.
var test = new ScrollGallery();
test.numSlide();
if you want to extend jQuery and use the function you can assign it as follows
$.fn.scrollGallery = new ScrollGallery();
and use it
$("window").scrollGallery.numSlide();
Can anyone tell me why my 'showDiv_boo' is undefined inside the class´s method?
I also can´t access my class´s methods.
Here´s my class 'Blink' class with its properties and methods:
function Blink(div) {
this.div = div
}
Blink.prototype.counter = 0
Blink.prototype.showDiv_boo = true
Blink.prototype.showDiv = function() {
this.div.style.visibility = 'visible'
}
Blink.prototype.hideDiv = function() {
this.div.style.visibility = 'hidden'
}
Blink.prototype.startEngine = function() {
if (this.showDiv_boo) {
this.showDiv()
} else if (!this.showDiv_boo) {
this.hideDiv()
}
this.showDiv_boo = !this.showDiv_boo
this.counter++
}
Blink.prototype.startEffect = function() {
this.idEffect = setInterval(this.startEngine, 1000 / 45)
}
So, if I create:
_blink = new Blink(myDiv);
_blink.startEffect();
You can test... the variable 'showDiv_boo', is undefined inside the method.
Even, if I set the showDiv_boo inside the method to true, it won´t call my class´s methods showDiv or hideDiv.
Anyone?
Thanks :)
The reason why is that startEngine is called from setInterval. The way in which this callback is invoked causes startEngine to have a different value for this than startEffect. You need to save this in order to maintain it in the callback. For example.
Blink.prototype.startEffect = function () {
var self = this;
self.idEffect = setInterval(function () { self.startEngine(); }, 1000 / 45);
};
You need to:
use var self and call the method via self.startEngine()
use an anonymous function to wrap the call in [1] i.e. function(){ self.startEngine(); }
This is because when you just pass this.startEngine or self.startEngine you are just passing the function startEngine without specifying what this is, which in both cases is supplied by the global conext of DOMWindow.
To give an example...
function startEngine() {
...code omitted...
};
Blink.prototype.startEngine = startEngine;
Blink.prototype.start = function() {
setTimeout(startEngine, 0); // obviously wrong, what is this?
setTimeout(Blink.startEngine, 0); // actually the same as line above, although not as obvious
setTimeout(startEngine.bind(this), 0); // works correctly
}
works to add code to the prototype and if used in the anonymous function will work as expected, but if you just use Blink.startEngine as the callback it is exactly the same as using startEngine only the second is more obviously wrong because there's no object it is being called on so you'd expect this to be whatever is supplied by the context.
The other way you could do this without using the anonymous function would be
Blink.startEngine.bind(self)
Which returns a function that will call startEngine with the correct this same as explicitly creating the anonymous function and wrapping the call to self.startEngine()
Heres a link to a fiddle to play around with the differences: http://jsfiddle.net/bonza_labs/MdeTF/
If you do the following, you will find it is defined
var x = new Blink('hello');
x.showDiv_boo
Javascript uses prototypical inheritance. While showDiv_boo may not be explicitly defined within the instance of Blink that you now have, it does exist within the prototype that Blink inherits from. When you try referencing showDiv_boo from within the object, the Javascript engine realizes the object does not own a member by that name and then will check its prototype.
Along with setting a temporal variable to store this, you must call the startEngine() function with that variable:
Blink.prototype.startEffect = function(){
var self = this;
self.idEffect = setInterval(function(){ self.startEngine.call(self); }, 1000/45);
}
Note the .call(self), which basically calls the function with the variable self, so the variable this in startEngine will be the correct one.