jQuery .each function not persisting global variable - javascript

I have a function whereby I wish to run a .each loop and return the running total of the values
At the end of my .each function, the amountSold variable is 0 and during the .each loop, it is shown as undefined. What am I doing wrong?
function processChange(currentTarget) {
var amountSold = 0; //VARIABLE DECLARED
//limit these operations to the currentlySelected tab
var availableFundContainer = $(currentTarget).closest(".available-content").parent();
var fundBeingSold = $(availableFundContainer.children(0)).attr('data-investment-code');
availableFundContainer.find('.available-handler').each(function (index, val) {
var origVal = $(this).attr('data-original-value');
var currentSliderVal = $(this).next('.available-content').find('.slider').slider("value");
var amountSold = amountSold + (origVal - currentSliderVal); //PROBLEM LINE!!!!!
....}

You're creating a locally scoped variable, also called amountSold, inside the each callback function.
Replace
var amountSold = amountSold + (origVal - currentSliderVal); //PROBLEM LINE!!!!!
with
amountSold += (origVal - currentSliderVal); //PROBLEM LINE!!!!!

You are confused with how the function scope works in JS. Consider the following example:
var someArray = [0,1,2,3,4,5];
var a = 0;
var b = 0;
someArray.forEach(function(item) {
a = a + item;
var b = b + item;
console.log("loop", a, b);
});
console.log("final", a, b);
Run the snippet, and you will see that within the loop, the variable b starts out as undefined. This is so, because the function passed to the forEach method creates a new scope, and any variables declared with the var statement, will override any variables that exist in the parent scope. In this case, it means that b ends up being NaN. On the other hand, a keeps its previous value and increments as expected.
The final console log demonstrates, that the value of b in the global scope was not changed by the statements executed in the loop.
This feature of JS allows a user to create a functions that do not have side effects on the global scope.
And to answer your question, you are using the var statement:
var amountSold = amountSold + (origVal - currentSliderVal);
when you should be using a simple assignment
amountSold = amountSold + (origVal - currentSliderVal);

Make the variable global using
window.amountSold = 0
Inside the loop just call it the same way
window.amountSold += (origVal - currentSliderVal);

Related

Passing variables through addEventListener

I am trying to add event to all inputs, get the value (number) from those and insertHtml them into span elements.
This is my javascript code. I have no idea how to pass the variables.
var input_selector = document.querySelectorAll('.coins_n'); // input number elements
var price_selector = document.querySelectorAll('.price'); // span elements
for(var i = 0; i <= input_selector.length; i++) {
var input = input_selector[i];
var price = price_selector[i];
input.addEventListener('input', function(){
console.log(price); // not working
console.log(input); // not working
price.innerHTML = input.value; // not working
})
}
The problem here has to do with scoping of your variables. var is a weird one, whose scope isn't really limited to the block, but to the containing function. The following two are (essentially) the same:
var input;
var i = 0;
for(; i < input_selector.length; i++) input = input_selector[i];
and
for(var i = 0; i < input_selector.length; i++) var input = input_selector[i];
both create a variable named input in global scope, and then update that variable. That means that any functions that wants to read input later will just read the last version of input, and not the version at the time you defined the handler you would trigger later.
let, however, is block scoped, and your for loop is a block. So defining let input inside the for loop will mean that input is defined uniquely for every iteration of the loop, since every time the block gets executed a new scope is created for everything in there.
The same is true for var i = 0 in your for loop - any handler that calls it later will just log the last global value of i, but if you use let, that's not the case and every iteration of the loop has its own i. So your code could simply be reduced to this:
const input_selector = document.querySelectorAll('.coins_n');
const price_selector = document.querySelectorAll('.price');
for( let i = 0; i < input_selector.length; i++ ){
input_selector[i].addEventListener('input', event => {
price_selector[i].innerHTML = event.target.value;
});
}
This is pretty complex to explain once you start typing it out, so better read what other have already written at something like MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Ok. I've managed it by using let.
var input_selector = document.querySelectorAll('.coins_n');
var price_selector = document.querySelectorAll('.price');
for(var i = 0; i < input_selector.length; i++) {
let input = input_selector[i];
let price = price_selector[i];
input_selector[i].addEventListener('input', function(){
price.innerHTML = this.value;
})
}

Using a variable increment to create new variables in Javascript

It might be a beginner's question, but I can't seem to find an answer on this.
The data it is getting is data out of a JSon file. I want it to loop through all the rows it is seeing. The loop works how it is written below and returns me the info I need with the rest of the code. I am trying to create multiple variables like testVar1, testVar2, testVar3, .... I don't know if it is possible to do it this way, or if I need to find another solution.
var i = 0;
for (var x in data) {
var testVar1 = data[0][1]; // works
var testVar[i] = data[0][1]; // doesn't
i += 1;
}
How can I make the testVar[i] work ?
What is the correct syntax for this?
Your code misses the initialization of your array variable: var testVar = [];.
⋅
⋅
⋅
Anyway, you may want to create those variables in the window object :
for (var i = 0; i <= 2; i++) {
name = 'var' + i;
window[name] = "value: " + i;
}
console.log(var0);
console.log(var1);
console.log(var2);
That way you can keep using the "short" variable name.
You can wrap all those variables in an object.
instead of:
var testVar1 = data[0][1];
Try:
var Wrapper = {};
//inside the for loop:
Wrapper["testVar" + i] = data[0][i];
...and so on.
You'd access them as Wrapper.testVar1 or Wrapper["testVar" + 1].
The problem you're having is pretty simple. You try to declare a variable as an array and in the same statement try to assign assign a value to a certain index. The reason this doesn't work is because the array needs to be defined explicitly first.
var testVar[i] = data[0][1];
Should be replaced with:
var testVar = []; // outside the loop
testVar[i] = data[0][1]; // inside the loop
Resulting in:
var i = 0,
testVar = [],
data = [
['foo', 'bar', 'baz'],
['kaas', 'is', 'baas']
];
for (var x in data) {
var testVar1 = data[0][1];
testVar[i] = data[0][1];
i += 1;
}
console.log('testVar1', testVar1);
console.log('testVar', testVar);
console.log('testVar[0]', testVar[0]);
console.log('testVar[1]', testVar[1]);
If i isn't an integer you should use an object instead. This can be seen in the answer of Tilepaper, although I advise against the use variables starting with a capital letter since they suggest a constant or a class.

Is there a way to loop this?

Is there a way to loop a declaration of a variable? just a loop to help me declare the variables so i dont have to do the monotonous work of change the numbers of the variable
var height1 = document.getElementById('height1').value;
var height2 = document.getElementById('height2').value;
var height3 = document.getElementById('height3').value;
var height4 = document.getElementById('height4').value;
var height5 = document.getElementById('height5').value;
var height6 = document.getElementById('height6').value;
var height7 = document.getElementById('height7').value;
var height8 = document.getElementById('height8').value;
var height9 = document.getElementById('height9').value;
var height10 = document.getElementById('height10').value;
var height11 = document.getElementById('height11').value;
var height12 = document.getElementById('height12').value;
var height13 = document.getElementById('height13').value;
var height14 = document.getElementById('height14').value;
var height15 = document.getElementById('height15').value;
var height16 = document.getElementById('height16').value;
This is not a right way of coding that, Just do like,
var heights = [];
Array.from(document.querySelectorAll("input[id^=height]")).forEach(function(itm){
heights.push(itm.value);
});
And now you can iterate the array heights to manipulate the values as per your requirement.
The logic behind the code is, querySelectorAll("input[id^=height]") will select the input elements that has id starts with the text height. Since the return value of querySelectorAll is a nodelist, we have to convert it as an array before using array functions over it. So we are using Array.from(nodelist). That will yield an array for us. After that we are iterating over the returned array by using forEach and pushing all element's value into the array heights.
This is almost always an indication that you want an array. Something like this:
var heights = [];
for (var i = 1; i <= 16; i++) {
heights.push(document.getElementById('height' + i).value);
}
Then you can reference a value from the array with something like:
heights[1]
Though technically since in JavaScript your window-level variables are indexable properties of the window object, you can essentially do the same thing with variable names themselves:
for (var i = 1; i <= 16; i++) {
window['height' + i] = document.getElementById('height' + i).value;
}
Then you can still use your original variables:
height1
Though in the interest of keeping things outside of window/global scope, maintaining the array seems a bit cleaner (and semantically more sensible).
This seems to be a good use case for an object:
var heights = {};
for (var i = 1; i <= 16; i++) {
heights[i] = document.getElementById('height' + i).value;
}
Maybe its time to introduce function:
Generally speaking, a function is a "subprogram" that can be called by code external (or internal in the case of recursion) to the function. Like the program itself, a function is composed of a sequence of statements called the function body. Values can be passed to a function, and the function will return a value.
function getHeight(id) {
return document.getElementById(id).value;
}
Call with the wanted id and use it like a variable.
getHeight('height1')
Normally you would put them in an array.
var heights = []
for (i = 1; i < 17; i++) {
heights[i] = document.getElementById('height' + i).value;;
}
Beware this will give you a hole at the start of the array ie heights[0] has nothing in it. If you use this to iterate it won't matter...
for (var i in heights) {
alert(heights[i]);
}

Modify a global variable based on an object property

I'm trying to learn object oriented javascript and ran into the following problem:
I have an object constructor (is that the right term?) like this:
function itemCreator(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
allItems.push(this); //store all items in a global variable
}//end itemCreator;
//then I use it to create an object
megaRocket = new itemCreator (
'megarocket',
'item_megarocket',
108,
475
)
Now I realised I also need to map these objects to modify different global variables based on which "itemType" the object has. This is where I am stuck. How can I make a global variable that only objects with a specific itemType property can modify?
For example I would like to create an object that increments a variable called amountOfMegarockets, but only if the itemType for that object is "item_megarocket".
I later plan on looping an array of these items to see if player object touches them (to collect the item):
function checkForItems(){
var itemLen =allItems.length;
for (i=0; i < itemLen; i++){
var itemObject = allItems[i];
if ( //checking for "collisions" here
(ship.x < (itemObject.itemBitmap.x + itemObject.size) && (ship.x + shipWidth) > itemObject.itemBitmap.x) &&
(ship.y < (itemObject.itemBitmap.y + itemObject.size) && (ship.y + shipWidth) > itemObject.itemBitmap.y)
){
itemObject.actor.y = -500; //just removing the item from canvas here (temporary solution)
// Here comes pseudo code for the part that I'm stuck with
variableBasedOnItemObject.itemType++;
}
I hope my explanation makes sense to someone!
EDIT:
Bergi's answer makes most sense to me, but I can't get the syntax right. Here's how I'm trying to use Bergi's code:
var amounts = {},
allItems = [];
function itemCreator(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
(amounts[itemType]=2); // this is different from bergi's example because I need to set the initial value of the item to two
//I also shouldn't increase the item amount on creation of the item, but only when it's specifically called from another function
this.increaseCount = amounts[itemType]++; //this should IMO increase the itemType amount inside the amounts object when called, but it doesn't seem to work
}
//creating the object the way bergi suggested:
allItems.push(new itemCreator('shootUp001', 'item_up', 108, 475));
Now here's the problematic part:
function checkForItems(){
var itemLen =allItems.length;
for (i=0; i < itemLen; i++){
var itemObject = allItems[i];
if ( my condition here)
){
//code below is not increasing the value for the current itemType in the amounts object.
//Probably a simple syntax mistake?
itemObject.itemType.increaseCount;
}
}
}
Why is my call of itemObject.itemType.increaseCount; not increasing the value of amounts.itemType?
Increment a global variable called amountOfMegarockets, but only if the itemType for that object is "item_megarocket".
Don't use a global variable for each of those item types. Do use one object (in global or local scope) which counts the amouts of each type on its properties.
var amounts = {},
allItems = [];
function Item(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
amounts[itemType]++ || (amounts[itemType]=1); // count by type
allItems.push(this); // store all items
}
Notice that I wouldn't put all Item instances in an array by default, better omit that line and let it do the caller:
allItems.push(new Item('megarocket', 'item_megarocket', 108, 475));
you can do some things like
(function (global) {
// create a scope
var allItems = [];
global.itemCreator = function(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
allItems.push(this); //store all items in a global variable
}//end itemCreator;
})(this);
this will work. but seriously dont complicate too mutch your code juste to make private var. if someone want to cheat using debugger he will always found a way to do it.
---- edit
If you juste want access to global var base on itemType you can do some thing like:
function checkForItems(){
var itemLen =allItems.length;
for (i=0; i < itemLen; i++){
var itemObject = allItems[i];
if ( //checking for "collisions" here
(ship.x < (itemObject.itemBitmap.x + itemObject.size) && (ship.x + shipWidth) > itemObject.itemBitmap.x) &&
(ship.y < (itemObject.itemBitmap.y + itemObject.size) && (ship.y + shipWidth) > itemObject.itemBitmap.y)
){
itemObject.actor.y = -500; //just removing the item from canvas here (temporary solution)
// Here comes pseudo code for the part that I'm stuck with
if (window[itemObject.itemType + "Data"])) {
variableBasedOnItemObject.itemType++;
} else {
window[itemObject.itemType + "Data"] = {
itemType: 1
}
}
}
}
}

How do I declare and use dynamic variables in JavaScript?

Suppose I need to declare a JavaScript variable based on a counter, how do I do so?
var pageNumber = 1;
var "text"+pageNumber;
The above code does not work.
In JavaScript (as i know) there are 2 ways by which you can create dynamic variables:
eval Function
window object
eval:
var pageNumber = 1;
eval("var text" + pageNumber + "=123;");
alert(text1);
window object:
var pageNumber = 1;
window["text" + pageNumber] = 123;
alert(window["text" + pageNumber]);
How would you then access said variable since you don't know its name? :) You're probably better off setting a parameter on an object, e.g.:
var obj = {};
obj['text' + pageNumber] = 1;
if you -really- want to do this:
eval('var text' + pageNumber + '=1');
I don't think you can do it sing JavaScript.I think you can use an array instead of this,
var textArray=new Array();
textArray[pageNumber]="something";
Assuming that the variable is in the global scope, you could do something like this:
var x = 1;
var x1 = "test"
console.log(window["x" + x]); //prints "test"
However, a better question might be why you want such behaviour.
You could also wrap your counter in an object:
var PageNumber = (function() {
var value = 0;
return {
getVal: function(){return value;},
incr: function(val){
value += val || 1;
this['text'+value]=true /*or some value*/;
return this;
}
};
})();
alert(PageNumber.incr().incr().text2); //=>true
alert(PageNumber['text'+PageNumber.getVal()]) /==> true
It can be done using this keyword in JS:
Eg:
var a = [1,2,3];
for(var i = 0; i < a.length; i++) {
this["var" + i] = i + 1;
}
then when you print:
var0 // 1
var1 // 2
var2 // 3
I recently needed something like this.
I have a list of variables like this:
var a = $('<div class="someHtml"></div>'),b = $('<div class="someHtml"></div>'),c = $('<div class="someHtml"></div>');
I needed to call them using another variable that held a string with the name of one of these variables like this:
var c = 'a'; // holds the name of the wanted content, but can also be 'b' or 'c'
$('someSelector').html(eval(c)) // this will just use the content of var c defined above
Just use eval to get the variable data.
I just did
I know a lot of the other answers work great, such as window["whatever"] = "x"; but I will still put my own answer here, just in case it helps.
My method is to use Object.assign:
let dict = {};
dict["test" + "x"] = "hello";
Object.assign(window, dict)
a little improvement over bungdito's answer, use the dynamic variable dynamically
var pageNumber = 1;
eval("var text" + pageNumber + "=123456;");
eval(`alert(text${pageNumber})`);
note: usage of eval is strongly discourgae

Categories

Resources