How do you use Javascript constructor methods in a jQuery statement? - javascript

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);
};
};

Related

Setting "this" to the instance, in a callback set during the creation of a prototype function

I have this code:
var createAllAreSelectedClickedHandler = function(selectablesArrayGetter) {
return function() {
var array = selectablesArrayGetter();
var desiredState = array.every(function(selectable) { return selectable.selected; }) ? false : true;
array.forEach(function(selectable) {
selectable.selected = desiredState;
});
};
};
Followed by this one:
function PromoViewModel() { this.registrations = [...] }
PromoViewModel.prototype.allEventsSelectedClickedHandler = createAllAreSelectedClickedHandler(function() { return this.registrations; }));
I can't manage to set the correct value of this. The "this" value when the function is created points to Window so I can't do .bind(this). I've tried doing .bind(PromoViewModel.prototype) but it lacks all the precious instance fields set inside the constructor.
I know I could simply set this.allEventsSelectedClickedHandler in the constructor function, but I'm trying to separate the methods creation from the variables.
The problem is the call selectablesArrayGetter(); which determines the this value for the callback.
You will need to "pass" the this value that the method (i.e. the closure you are returning) is invoked on, using call:
var array = selectablesArrayGetter.call(this);
I'd recommend defining your PromoViewModel.prototype.allEventsSelectedClickedHandler method as follows:
PromoViewModel.prototype.allEventsSelectedClickedHandler = function() {
var _array = this.registrations;
var desiredState = _array.every(function(selectable) { return selectable.selected; }) ? false : true;
_array.forEach(function(selectable) {
selectable.selected = desiredState;
});
};
the function that you're passing as callback uses this, but doesn't have the PromoViewModel context. You can ensure the method has the proper context by binding this to a variable.
function PromoViewModel()
{
var me = this;
this.registrations = [...];
this.allEventsSelectedClickedHandler = createAllAreSelectedClickedHandler(function() {
return me.registrations;
});
}
Working fiddle: http://jsfiddle.net/michaschwab/coegnL5j/9/ also has Bergi's answer in there (commented out) to show that that works just as well.
Ok here is what I did.
In the prototype definition instead of directly associating it to createAllAreSelectedClickedHandler function, I actually define a function that returns the createAllAreSelectedClickedHandler function. By doing this, I can define a variable (in this case protoScope) that maps this context when defined.
When doing that, if you put a break-point in the createAllAreSelectedClickedHandler function you will see that the selectablesArrayGetter value is correct (the acutal registrations array).
PromoViewModel.prototype.allEventsSelectedClickedHandler = function (){
var protoScope = this;
return createAllAreSelectedClickedHandler(function() {
return protoScope.registrations;
});
}

Manipulate object's property while in event handler

I've learned that for scope reasons the this keyword inside an event listener, which is embedded in an object, doesn't refer to the global object but rather to the element which triggered the event.
Now, I understand that if I want to fetch a property I can save it to a variable before the event handler is called. But what can I do if I want to manipulate the property's value?
In the following piece of code I am trying to manipulate the drugCount property within the removeDrug event listener.
var Drugs = {
drugs: $("#drugs_table"),
drugRow: $("#drug").html(),
drugCount: 0,
init: function() {
this.addDrugRow();
this.removeDrugRowHandler();
},
addDrugRow: function() {
this.drugCount++;
this.drugs.append(this.drugRow.replace(/{{id}}/,this.drugCount));
$(".drugsSelect").select2();
},
removeDrugRowHandler: function() {
drugCount = this.drugCount;
// also a problematic solution, because it only retains the inital drugCount.
// i.e I need a way to access the "live" count from within the event
$(document).on("click",".removeDrug",function(){
if (drugCount>0) {
$(this).parents("tr").remove();
this.drugCount--; // how should I approach this?
}
});
}
}
Try This
var Drugs = function() {
var me = this;
me.drugs = $("#drugs_table");
me.drugRow = $("#drug").html();
me.drugCount = 0;
me.init = function() {
this.addDrugRow();
this.removeDrugRowHandler();
};
me.addDrugRow = function() {
this.drugCount++;
this.drugs.append(this.drugRow.replace(/{{id}}/,this.drugCount));
$(".drugsSelect").select2();
};
me.removeDrugRowHandler= function() {
var drugCount = me.drugCount;
$(document).on("click",".removeDrug",function(){
if (drugCount>0) {
$(this).parents("tr").remove();
me.drugCount--;
}
});
}
}
As it turns out the easy solution is to use the object name instead of the contextual this.
So instead of this.drugCount I used Drugs.drugCount.
However, this solution only works if I am in the context of a single object. If I were to write a "class" (i.e var Drugs = function(){ ... }) this won't work.

Getting Value from Nested Anonymous Function

I've got a list of items that have different data values that I'm trying to return on a click event. I can't seem to pass the value from the click event to the parent function. I may just be doing this all wrong. I just need the data value from each click event to replace the value of my systemName variable. Can someone help me out here?
function getSystemName(){
var systemName = '';
$('.pulldown li').bind('click', function(){
var systemName = '';
var systemName = $(this).find('a').data('value');
console.log(systemName);
});
return systemName;
}
// Edit: I should probably explain that I'm trying to get that value from the click event to update content on the page via jquery .load so perhaps like mentioned below....I'm not doing this properly because I can log the systemName properly on click but it doesn't seem to update the page content. Here is the entire code block.
$.ajaxSetup ({
cache: false
});
var ajax_load = "<img src='.../images/130.gif' alt='loading...' align='middle' />";
var loadUrl = ".../content.php";
var getSystemName = (function(){
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});
return function() { return systemName };
})();
$("#results")
.html(ajax_load)
.load(loadUrl, "ki_systems=" +getSystemName());
In the code in the question, each time you call getSystemName() it will bind a click handler again (resulting in multiple click handlers) and then just return the local systemName variable that will always be blank.
If you are saying that each time you call getSystemName() you want it to return the system name associated with whichever li element was most recently clicked then you can declare the systemName variable as a global variable and bind the event handler (once) outside getSystemName():
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});
function getSystemName() {
return systemName;
}
But that makes the function rather redundant: you could just refence the global variable directly.
On the other hand you could declare just the function and use a closure to avoid the need for global variables, something like this:
var getSystemName = (function(){
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});
return function() { return systemName };
})();
// if no items have been clicked yet it will return an empty string by default
console.log( getSystemName() ); // ""
// click on an item with name "red"
console.log( getSystemName() ); // "red"
// click on an item with name "green"
console.log( getSystemName() ); // "green"
That last block could be a little confusing to the uninitiated. It is setting getSystemName to whatever is returned from an anonymous function expression that is executed immediately (note the extra empty parentheses at the end). And what is returned is a simple function that when called itself will return systemName, which is declared in the outer scope. The click handler also updates this same systemName. So when you actually call getSystemName() you are calling the little one-liner function from the return statement.
I think the problem here is that you're missing synchronous and asynchronous programming - you probably want a callback on the getSystemName() function. I expect there's something more architecturally wrong with the approach you're taking.
However, this is what it'd look like if I wrote that using your style:
function getSystemName(callback) {
$('.pulldown li').bind('click', function(){
console.log(systemName);
callback( $(this).find('a').data('value') );
});
}
So instead of this sort of code:
console.log("System Name: ", getSystemName())
You would have:
getSystemName( function (name) { console.log("System Name: ", name); } );
Remove the var from the second and third definitions. If you use var before the variable name a new local instance will be created.
systemName = '';
systemName = $(this).find('a').data('value');
console.log(systemName);
Consider this, on the second line you're only binding a callback function to the click event, not executing the code inside that function.
You can't.
Look at the code.
Set systemName to an empty string
When a list item is clicked set systemName to something else
Return systemName
You can't make 3 wait for 2. You need to do something with the data in the click handler and not try to return it.
Well, i suppose you are trying to set a value to see which node has been click, thus you need to change the scope of your systemName variable. Move it out so it will be global, like so:
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});

Changing the state of a toggle in JavaScript/jQuery

Is it possible to change the state of a toggle function? Like:
myDiv.toggle ... function 1 , function 2
I click on the myDiv element, the function 1 executes
I click again, function 2
I click again, function 1
BUT
Change the state
function 1 again
etc.
But I need to be able to change the state from outside the toggle function.
Here is a javascript object that uses closure to track it's state and toggle:
var TOGGLER = function() {
var _state = true;
var _msg = "function1";
var function1 = function() {
_msg = "function1";
}
var function2 = function() {
_msg = "function2";
}
return {
toggle: (function () {
_state = !_state;
if (_state) {
function1();
} else {
function2();
}
return _msg;
})
}
}();
Here is a jsfiddle that shows how to use it to toggle based with the following jquery: http://jsfiddle.net/yjPKH/5/
$(document).ready(function() {
$("#search").click(function() {
var message = TOGGLER.toggle();
$("#state").text(message);
});
});
The toggle function is meant for simple use cases. Changing the state externally is not "simple" anymore.
You cannot easily/safely (it's internal so it may change during minor versions) access the state variable of the toggle function easily as it's stored in the internal dataset of the element.
If you really want to do it, you can try this code though:
$._data(ELEMENT, "lastToggle" + func.guid, 0);
func is the function you passed to .toggle(), so you need to save this function in a variable. Here's a minimal example: http://jsfiddle.net/xqgrP/
However, since inside the function there's a var guid = fn.guid || jQuery.guid++ statement, I somehow think that the devs actually meant to use guid instead of func.guid for the _data key - in that case a minor update is very likely to break things. And after the fix you'd have to iterate over the data set to retrieve the correct key as there is no way to access the guid from outside.

Javascript Class Inheritance

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.

Categories