Can't update all variables from function - javascript

this is my JS:
var money = 4;
var thirst = 50;
function satisfy(what,how,price,sBar){
if(price<=money){
what=what+how;
money=money-price;
updateBar(sBar,what);
updateMoney();
} else {
log.unshift("D"+day+" "+hour+":"+minute+" - Poor hobo, you don't have enough money for that. <br>");
updateLog();
};
};
And this is in my html
<a onClick="satisfy(thirst,30,0.84,'#thirst')";>buy</a>
After I click it problem is that global variable for thirst doesn't get updated but money global variable does get updated. How can I fix it to make thirst global variable updated too?
Thank you, very much.

This is because JavaScript numbers are passed by value, not by reference, meaning that a copy of thirst is created and modified. Why not return a value instead:
HTML:
<a id="buy-link">buy</a>
JavaScript:
var money = 4;
var thirst = 50;
function satisfy(what, how, price, sBar) {
if (price <= money){
what += how;
money -= price;
updateBar(sBar,what);
updateMoney();
} else {
log.unshift("D" + day + " " + hour + ":" + minute + " - Poor hobo, you don't have enough money for that. <br>");
updateLog();
}
return what;
}
var buyLink = document.getElementById("buy-link");
buyLink.addEventListener("click", function() {
thirst = satisfy(thirst, 30, 0.84, '#thirst')
}, false);
I also removed some of your unneeded semicolons, converted your event handler into using the standard addEventListener function, and cleaned up your code a bit.

If you absolutely had to "pass by reference" you could pass an object then modify the contents inside the function.
var money = 4,
thirst = 50,
myObj = {
what: 'thirst',
how: 30,
price: 0.84,
sBar: '#thirst'
}
function doSomething(obj) {
obj.what = 'hunger';
obj.how = 20;
}
doSomething(myObj);
console.log(myObj.what); // "hunger"

add
thirst=what;
after:
updateMoney();
however, since you're using it as a parameter, I suspect you're planning to pass different variables besides "thirst" to the function
in that case, just pass a string, like "thirst". then in your function have something like
if (what=="thirst")
thirst=what;
if (what=="hunger")
hunger= what;
etc

Related

JavaScript scope issue/error

I got at brainteaser in my mailbox, it is supposed to take 20 minutes but apparently, I got stuck at scope that I crashed Chrome. The idea is that a string is provided to you. You then use the string to generate random sentences akin to lorum ipsum.
var words = "The sky above the port was the color of television, tuned to a
dead channel. All this happened, more or less. I had the story, bit by bit,
from various people, and, as generally happens in such cases, each time it
was a different story. It was a pleasure to burn.";
var wordList = words.split(' ');
var numWords = getRandomInt(2, 8);
var numSentinces = getRandomInt(8, 40);
var sentinces = [];
var sentince = [];
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function genSentinces() {
while (numWords > 0) {
sentince.push(wordList[getRandomInt(0, wordList.length)]);
numWords--;
}
sentince = sentince.join(' ');
console.log(sentince)
return sentince;
}
genSentinces();
genSentinces();
I am assuming that the scope of the sentence variable is wrong as it runs the first time but not the second time. I think I need to add a this somewhere.
Any help would be appreciated, as I can read code that has this in it but I apparently can't write code with this yet.
The main mistake is that you forget, that if you will modify global variable (all variables outside your functions can be called "global" regarding to this functions), it will not take the original value without your intervention. For example, if you declare new variable outside the function like var x = 0;, and then modify this variable from within the function like x = 1, this variable will be equal to 1 now.
You init sentince variable as array (var sentince = [];), but after the first execution of genSentinces function, this variable will be a string (because you're doing sentince = words.join(' ')). For this reason, I declared new array words inside the function, and I push words to it instead of pushing to global sentince array.
You use numWords-- to decrease the counter on every loop iteration, but numWords is a global variable and it will still equal to 0 after the first function invocation (it's why I added numWords = getRandomInt(2, 8) after the loop).
Here is the working example, feel free to ask, if anything isn't clear:
var words = "The sky above the port was the color of television, tuned to a dead channel. All this happened, more or less. I had the story, bit by bit, from various people, and, as generally happens in such cases, each time it was a different story. It was a pleasure to burn.";
var wordList = words.split(' ');
var numWords = getRandomInt(2, 8);
var numSentinces = getRandomInt(8, 40);
var sentinces = [];
var sentince = [];
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function genSentinces() {
var words = [];
while (numWords > 0) {
words.push(wordList[getRandomInt(0, wordList.length)]);
numWords--;
}
numWords = getRandomInt(2, 8);
sentince = words.join(' ');
console.log(sentince)
return sentince;
}
genSentinces();
genSentinces();
You changed variable 'sentince' from array to string and when you call function second time you call 'sentince.push(...' to string type and variable variable 'numWords' equal to 0 after first call.

JavaScript, accessing a global variable through a function

Before I specify what I am struggling with, let me show you my JavaScript codes.
var gVar = 0;
$("#plus").click(function(e) {
e.preventDefault();
gVar = gVar + 1;
alert(gVar);
});
$("#minus").click(function(e) {
e.preventDefault();
gVar = gVar + 1;
alert(gVar);
});
In the code above, I first set a global variable 'gVar' that holds a integer value of zero. Whenever I click a button whose ID is "plus", the value of the global variable 'gVar' is going to increment by 1, which will be printed out in a browser's dialogue box.
The same thing will happen for the button whose ID is "minus" except that the value of 'gVar' is going to decrement by 1.
In my attempt to tidy up the code above, I created a function separately, which will be called whenever I click the two buttons. Here are the code.
var gVar = 0;
var weird = function (button, Var, num1) {
$(button).click(function(e){
e.preventDefault();
Var = Var + num1;
alert(Var);
});
};
weird("#plus", gVar, 1);
weird("#minus", gVar, -1);
When I run this code, the value of 'gVar' never changes and always stays '0'.
I think I vaguely know what the issue here is, but not entirely sure what is causing this problem. Any input will greatly be appreciated to clarify this issue for me.
Also, I am curious as to if there is any way to create a function to achieve the same effect instead of writing a similar set of code twice for the two different click events.
In JavaScript, only objects are passed as a reference, all other types, including numbers, are copied when you assign them to another variable.
You can instead pass your variable name as a string and then refer to it as window[Var]:
var gVar = 0;
var weird = function (button, Var, num1) {
$(button).click(function(e){
e.preventDefault();
window[Var] = window[Var] + num1;
alert(window[Var]);
});
};
weird("#plus", "gVar", 1);
weird("#minus", "gVar", -1);
As #shiplu.mokadd.im suggested, you can also change gVar to object:
var gVar = {value: 0};
var weird = function (button, Var, num1) {
$(button).click(function(e){
e.preventDefault();
Var.value = Var.value + num1;
alert(Var.value);
});
};
weird("#plus", gVar, 1);
weird("#minus", gVar, -1);
No matter what the function is, you cannot reassign an argument and expect the scope to reflect that change. Depending on the argument however, you can modify it (i.e. if gVar were an object or an array). I suggest you read about pass by reference/value to gain a better understanding of what this means here
the essence of a global variable is that it is global to the whole program, and as such - doesn't need to be passed as argument to any function.
so without modifying too much of your original code
var gVar = 0;
var weird = function (button, num1) {
$(button).click(function(e){
e.preventDefault();
gVar += num1;
alert(Var);
});
};
weird("#plus", 1);
weird("#minus", -1);
var gVar = {
value: 0
};
var weird = function(button, Var, num1) {
$(button).click(function(e) {
e.preventDefault();
Var.value = Var.value + num1;
alert(Var.value);
});
};
weird("#plus", gVar, 1);
weird("#minus", gVar, -1);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" value="+" id="plus">
<br>
<input type="button" value="-" id="minus">

Can I access a variable's name as a string after passing it as an argument to another function?

Perhaps a slightly dim question but I'm trying to design something where I'm using javascript / jquery to change the layout of a website and I'd like to see the values of both a variable name and it's current value in another div.
I was just doing $test.append('example string' + exampleVar) a lot so I thought I would make a function called test().
So far I have:
function test (test) {
$test = $('.test');
$test.append(test+"<br>");
}
and then would pass it a variable name as an argument but I can't find any way of making it display the name as a string. I know about making it as an object to access the key and value but that doesn't really seem to work here?
Bit of a long-winded way to do it, but here's an example using an object:
function tester(options) {
var keys = Object.keys(options);
console.log(keys[0] + ': ' + options[keys[0]]); // test: My value
}
tester({ test: 'My value' });
DEMO
You could use a feature of javascript where obj["prop"] is the same as obj.prop
So instead of passing the variable as a variable and hoping to get its name, you use the name as a string to get the variable's value.
If you aren't using namespaces/variables and want to a global/root variable, pass window, eg:
function test(obj, val) {
console.log(val + ": " + obj[val]);
}
var val1 = 1;
var val2 = 2;
test(window, "val1");
test(window, "val2");
(obviously you don't get the name of 'obj' - but maybe it's a start)
if you only have root/global variables (as in the example provided in the question) then you could remove obj:
function test(val) {
console.log(val + ": " + window[val]);
}
var val1 = 1;
var val2 = 2;
test("val1");
test("val2");
It seems what you want to do something like this:
var argumentName = /([^\s,]+)/g;
// fu is a function
// fu.toString look like: function myFunction (param[, param]*) { code }
function getParamNames(fu) {
var f = fu.toString();
return f.slice(f.indexOf('(')+1,f.indexOf(')')).match(argumentName);
}
Then you might want to create an helper which will take every functions:
// I choosed the console as an output, but you can set anything
function displayParameters(fu) {
var _params = getParamNames(fu);
if(_params!==null) {
for(var i=0;i<_params.length; i++) {
console.log(_params[i]);
}
} else { console.log('no parameters'); }
}
And, you will need to call: displayParameters(test);
In a function you will be using a parameter. So in that case the "variable name" will always be the name of the parameter. In this case getting the string value will always be test. I would assume this is not what you want. You were correct that the best way to do this is to use an object and iterate over the key, values. You would do this like:
var test = {
"test" : "value"
};
function test (test) {
var k, $test = $('.test');
for(k in test){
$test.append(k + "<br>");
}
}
Also, I do not think there is a way to get the variable string name. So the above would be the way to get the name of a variable.

JavaScript function will only work once

I have a JavaScript function that is triggered onchange. What it does is take the value from a input field and then add it to the value entered in another field and then display the answer in a different field.
It works fine the first time, but when I enter a new value and leave the field there is an error: TotalPurchasePrice is not a function
function TotalPurchasePrice(BuyinPrice, TopupAmount) {
var BuyinPrice
var TopupAmount
BuyinPrice = BuyinPrice.toString().replace(/\£|\,/g, '');
TopupAmount = TopupAmount.toString().replace(/\£|\,/g, '');
TotalPurchasePrice = (BuyinPrice * 1) + (TopupAmount * 1);
document.getElementById('tTotalPurchasePrice').value = TotalPurchasePrice;
}
Can anyone tell me why this would only work once?
This line :
TotalPurchasePrice = (BuyinPrice * 1) + (TopupAmount * 1);
replaces the TotalPurchasePrice function by a number. Then it's not a function, hence the error you have.
Use a different variable name :
var totalPrice = (BuyinPrice * 1) + (TopupAmount * 1);
document.getElementById('tTotalPurchasePrice').value = totalPrice;
You could also simply have added the var keyword but it would have made the code confusing.
A way to reduce the probability of such errors is to follow best practices when naming functions and variables. Here you should have named your function as a verb to denote an action instead of a value.
You're are confusing JavaScript with VBScript here.
To return value use return keyword:
return (BuyinPrice * 1) + (TopupAmount * 1);
Then outside the function have such line:
document.getElementById('tTotalPurchasePrice').value = TotalPurchasePrice(BuyinPrice, TopupAmount);
Assigning the value of a variable inside a function without stating it with var (TotalPurchasePrice in your case) means that the variable is global.
Since you could have the function declaration written as: var TotalPurchasePrice = function(BuyinPrice, TopupAmount) {}, you are just overriding it.
You could either rename the variable inside the function or add a var statement in front of it like:
var TotalPurchasePrice = (BuyinPrice * 1) + (TopupAmount * 1);

Javascript character limit counter function for text input

I've made a counter with javascript that shows a user how characters are remaining (from a set limit) for some text input or text area. Here's the code:
<script type="text/javascript">
function CountRemaining()
{
var limit = 1000;
var count = document.getElementById('press-form-body').value.length;
document.getElementById('counter').innerHTML = ((limit-count) + " characters left");
var timer = setTimeout("CountRemaining()",50);
}
</script>
My abomination above works fine but my problem is that I need to use this multiple times and making a separate function for every time I need it would be impractical to say the least.
I tried this and it didn't work:
<script type="text/javascript">
function CountRemaining(string, targetcounter, limit)
{
var count = document.getElementById(string).value.length;
document.getElementById(targetcounter).innerHTML = ((limit-count) + " characters left");
var timer = setTimeout("CountRemaining()",50);
}
I then figured I put the wrong statement for the timer so I changed it to this but still didn't work:
var timer = setTimeout("CountRemaining(string, targetcounter, limit)",50);
I'm lost. Any help would be highly appreciated. Thank you!
I think a better idea would be to use the "onchange" event for those types of elements.
Basically as soon as the text area / text input loses focus and is changed, you can bind a function to count how many characters are left.
document.getElementById('press-form-body').onchange = function() {
// your stuff (double check this to make sure the "this" value is right
// use this as an example
document.getElementById(targetcounter).innerHTML = this.value.length - 1000
}
Another solution would be to use the "key" events to listen to any keypress in the inputs.
document.getElementById('press-form-body').onkeypress = function() {
// your stuff (double check this to make sure the "this" value is right
// use this as an example
document.getElementById(targetcounter).innerHTML = this.value.length - 1000
}
function limittxt()
{
var tval = document.getElementById('press-form-body').value;
tlength = tval.length;
set = 100;
remain = parseInt(set - tlength);
document.getElementById('counter').innerHTML = remain + " characters left";
if (remain <= 0) {
document.getElementById('press-form-body').value = tval.substring(0, tlength - Math.abs(remain)))
}
}
An call this function in the input element like the following :
<input type='text' onkeypress='limittxt()' onkeyup='limittxt()' onkeydown='limittxt()'>
I suppose the error is with following line:
var timer = setTimeout("CountRemaining(string, targetcounter, limit)",50);
Here i think it should come like:
var str = "CountRemaining(" + string + "," + targetcounter + "," + limit + ")";
var timer = setTimeout(str,50);
If you want to proceed along the lines of using the timer to run the function at regular intervals, then you would need code similar to the following (hat-tip to #Sameera Thilakasiri for the inspiration):
function CountRemaining(string, targetcounter, limit){
var count = document.getElementById(string).value.length;
document.getElementById(targetcounter).innerHTML = ((limit-count) + " characters left");
}
setInterval(function() {
// call the function for each of the inputs on the page you need a counter for
CountRemaining('press-form-body', 'counter', 1000);
// etc
}, 50);
However, I believe #amchang87's approach is better overall, so I recommend you go with that if possible.
Tracking the number of characters left is always a little difficult. A good event to use is keyup or keypress, but that doesn't cover text that is dragged and dropped into the element, so people end up using a timer.
If you have many elements to monitor, consider putting them into an array, then call the timer at each interval and check all of the elements. Be careful with performance though, running the function every 50 ms may sap quite a bit of browser performance so try to keep the processing to an absolute minimum.
That means caching whatever you can and keep the logic simple.
Edit
The run and stop methods below could be used to start the timer when particular elements get focus, then stop it when they lose focus. That way you aren't hogging resources when not required.
/Edit
var keyCountCheck = (function() {
var elementArray, timerRef;
return {
// Initialise once
init: function() {
var input, inputs;
// Initialise elementArray if hasn't been done already
// If adding and removing elements, create new aray
// instead each time.
if (!elementArray) {
elementArray = [];
inputs = document.getElementsByTagName('input');
for (var i=0, iLen=inputs.length; i<iLen; i++) {
input = inputs[i];
if (input.type == 'text') {
elementArray.push(input);
}
}
}
timerRef = window.setInterval(keyCountCheck.run, 50);
},
// Run timer
run: function() {
// If setInterval not running, start it
if (!timerRef) {
keyCountCheck.init();
}
var el;
for (var i=0, iLen=elementArray.length; i<iLen; i++) {
checkLength(elementArray[i]);
}
},
// In case there is a reason to stop this thing.
stop: function() {
if (timerRef) {
window.clearTimeout(timerRef);
timerRef = null;
}
}
};
}());
window.onload = keyCountCheck.init;
function checkLength(el) {
// Character limit can be set as a data- attribute or
// class or various other ways. This is the simple way
var limit = 10;
var msgEl = document.getElementById(el.id + '_limitMsg');
if (msgEl) {
msgEl.innerHTML = (limit - el.value.length) + ' characters left. ' + (new Date());
}
}
Some supporting HTML to play with:
<input id="i0" value="1"><span id="i0_limitMsg"></span>
<br>
<input id="i1" value="2"><span id="i1_limitMsg"></span>
<br>
<button onclick="keyCountCheck.stop()">stop</button>
<button onclick="keyCountCheck.run()">run</button>
setInterval(
function CountRemaining(string, targetcounter, limit){
var count = document.getElementById(string).value.length;
document.getElementById(targetcounter).innerHTML = ((limit-count) + " characters left");
},50
);
Tryout this way.
Basic concept of this solution,
var f = function() {function_name(arg1); };
setTimeout(f, msec);

Categories

Resources