JavaScript, accessing a global variable through a function - javascript

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">

Related

how do i create a simple onclick add function()

i try to do these code and expecting it for increase by 1 everytime i click on the button but it returns me NaN instead.
im really new to javascript. really hope someone could help me!
thanks in advance.
function add(){
var sum = parseInt(1);
var adding = adding + sum;
document.getElementById("amt1").innerText = adding;
}
I see, here you've asked two problems:
Why adding is NaN
At line #2, you haven't initialized variable adding, hence in RHS adding is undefined.
Therefore, the RHS block adding + sum; is evaluated as undefined + 1, which evaluates to NaN
How to use onClick()
W3School's tutorial on onClick()
Here is your code in working state (HTML + JavaScript):
var adding = 0; // initialization. This is the step, that your code was missing
function add() {
var sum = parseInt(1);
adding = adding + sum;
document.getElementById("amt1").innerText = adding;
}
<h1>The onclick Event</h1>
<button onclick="add()">Click me</button>
<p id="amt1"></p>
You could take a closure over the sum and take the returned function for adding to the value.
var add = function(sum) {
return function () {
document.getElementById("amt1").innerHTML = ++sum;
};
}(0);
<span id="amt1">0</span> <button onclick="add()">add</button>
You're making the assignment in the local scope of the function, so every time the function executes, it's going to assign the value 1 to the 'sum' variable. Next, you're creating the variable 'adding' by trying to assign the value of adding, which doesn't exist yet.
It seems like the goal of your function is to just increment the value of 'amt1' by one.
function add(elId){
let currentAmt = document.getElementById(elId).innerText;
currentAmt = parseInt(currentAmt) + 1;
document.getElementById(elId).innerText = currentAmt;
}
By passing in the element ID, your function can now be applied to any element. It parses the integer from its current inner text, adds 1, then sets the new amount to the inner text of the element.
you might need to have a look on this post- Increment value each time when you run function
about how to increment, the idea is keep the variable outside the function as in you case,
you dont need parseInt as its used for parsing integers from a string.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
you need to keep the sum variable outside function. following is the general function to add
var n = 0;
function add(value){
n += value;
return n;
}
try it
document.getElementById("amt1").addEventListener("click", displayDate);
function displayDate() {
var node = document.getElementById('amt1');
document.getElementById("amt1").innerHTML = parseInt(node.textContent)+1;
}
<button id="amt1">1</button>
You're using an undefined variable adding to make your calculation that's why you get a NaN as a result :
var adding = adding + sum : the variable adding isn't yet initialized so it's value equals to undefined which give us var adding = undefined + sum = NaN. See next example :
var sum = parseInt(1);
console.log('typeof "adding" before initialization is ' + typeof adding + ' and it equals "' + adding + '"');
var adding = adding + sum;
console.log('typeof "adding" after initialization is ' + typeof adding + ' and it equals "' + adding + '"');
BTW, you don't need parseInt in order to put manually a number, sum = parseInt(1) is the same as sum = 1 but the later is faster.
And now here's how to accomplish your task :
/**
* btn: the button to be clicked to increment the counting.
* counterTxt: the "p#counter" element that prints the counting.
* counter: keeps track of the number of the clicks made.
**/
const btn = document.getElementById('btn'),
counterTxt = document.getElementById('counter');
let counter = 0; /** holds the number of clicks **/
/** click event handler for the "button" **/
btn.addEventListener('click', () => counterTxt.textContent = ++counter); /** update the "p#counter" text and increment the counter **/
<button id="btn">click to increment</button>
<p id="counter">0</p>

Javascript on click event not reading else statement or variables

I'm trying to make a click handler that calls a function; and that function gets a string and basically slices the last character and adds it to the front, and each time you click again it should add the last letter to the front.
It seem so easy at first that I thought I could just do it using array methods.
function scrollString() {
var defaultString = "Learning to Code Javascript Rocks!";
var clickCount = 0;
if (clickCount === 0) {
var stringArray = defaultString.split("");
var lastChar = stringArray.pop();
stringArray.unshift(lastChar);
var newString = stringArray.join('');
clickCount++;
} else {
var newArray = newString.split("");
var newLastChar = newArray.pop();
newArray.unshift(newLastChar);
var newerString = newArray.join("");
clickCount++;
}
document.getElementById('Result').innerHTML = (clickCount === 1) ? newString : newerString;
}
$('#button').on('click', scrollString);
Right now it only works the first time I click, and developer tools says newArray is undefined; also the clickCount stops incrementing. I do not know if it's an issue of scope, or should I take a whole different approach to the problem?
Every time you click you are actually reseting the string. Check the scope!
var str = "Learning to Code Javascript Rocks!";
var button = document.getElementById("button");
var output = document.getElementById("output");
output.innerHTML = str;
button.addEventListener("click", function(e){
str = str.charAt(str.length - 1) + str.substring(0, str.length - 1);
output.innerHTML = str;
});
button{
display: block;
margin: 25px 0;
}
<button id="button">Click Me!</button>
<label id="output"></label>
It is, in fact, a scoping issue. Your counter in inside the function, so each time the function is called, it gets set to 0. If you want a counter that is outside of the scope, and actually keeps a proper count, you will need to abstract it from the function.
If you want to keep it simple, even just moving clickCount above the function should work.
I do not know if it's an issue of scope
Yes, it is an issue of scope, more than one actually.
How?
As pointed out by #thesublimeobject, the counter is inside the function and hence gets reinitialized every time a click event occurs.
Even if you put the counter outside the function, you will still face another scope issue. In the else part of the function, you are manipulation a variable (newString) you initialized inside the if snippet. Since, the if snippet didn't run this time, it will throw the error undefined. (again a scope issue)
A fine approach would be:
take the counter and the defaultString outside the function. If the defaultString gets a value dynamically rather than what you showed in your code, extract its value on page load or any other event like change, etc. rather than passing it inside the function.
Do not assign a new string the result of your manipulation. Instead, assign it to defaultString. This way you probably won't need an if-else loop and a newLastChar to take care of newer results.
Manipulate the assignment to the element accordingly.
You can use Javascript closure functionality.
var scrollString = (function() {
var defaultString = "Learning to Code Javascript Rocks!";
return function() {
// convert the string into array, so that you can use the splice method
defaultString = defaultString.split('');
// get last element
var lastElm = defaultString.splice(defaultString.length - 1, defaultString.length)[0];
// insert last element at start
defaultString.splice(0, 0, lastElm);
// again join the string to make it string
defaultString = defaultString.join('');
document.getElementById('Result').innerHTML = defaultString;
return defaultString;
}
})();
Using this you don't need to declare any variable globally, or any counter element.
To understand Javascript Closures, please refer this:
http://www.w3schools.com/js/js_function_closures.asp

Can't update all variables from function

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

How do I dynamically create events and event handlers in javascript

I'm trying to create buttons dynamically with unique listeners and handlers by using a for loop, but unfortunately I must be doing something wrong because only the last button works.
Even more surprising is the fact that when clicking the last button instead of "Button No.3" it returns "Button No.4"
Bellow is the code and here is a jsfiddle http://jsfiddle.net/y69JC/4/
HTML:
<body>
<div id="ui">
some text ...
</div>
</body>
Javascript:
var uiDiv = document.getElementById('ui');
uiDiv.innerHTML = uiDiv.innerHTML + '<br>';
var results = ["Button one","Button two","Button three","Button four"];
for(var n=0;n<results.length;n++)
{
uiDiv.innerHTML = uiDiv.innerHTML + '<button id="connect'+n+'">option'+n+':'+results[n]+'</button><br>';
var tempId = document.getElementById('connect'+n);
tempId.addEventListener('click', function(){console.log("Button No."+n)}, false);
}
Thanks!
It's a classic case when you need a closure: Change to:
var uiDiv = document.getElementById('ui');
uiDiv.innerHTML = uiDiv.innerHTML + '<br>';
var results = ["Button one", "Button two", "Button three", "Button four"];
for (var n = 0; n < results.length; n++) {
// Note: do not use .innerHTML. Create new element programatically
// and then use .appendChild() to add it to the parent.
var button = document.createElement('button');
button.id = 'connect' + n;
button.textContent = 'option' + n + ':' + results[n];
uiDiv.appendChild(button);
button.addEventListener('click', /* this is an inline function */ (function (n2) {
// Here we'll use n2, which is equal to the current n value.
// Value of n2 will not change for this block, because functions
// in JS do create new scope.
// Return the actual 'click' handler.
return function () {
console.log("Button No." + n2)
};
})(n)); // Invoke it immediately with the current n value
}
The reason for this is that a for loop does not create a scope, so "Button No. " + n was always evaluated with the n equal to the number of elements in results array.
What has to be done is to create an inline function accepting n as a parameter and call it immediately with the current value of n. This function will then return the actual handler for the click event. See this answer for a nice and simple example.
Edit: Your code is using innerHTML property to add new buttons in a loop. It is broken because every time you assign using uiDiv.innerHTML = ..., you are deleting all contents present previously in this div and re-creating them from scratch. This caused ripping off all event handlers previously attached. Schematically, it looked like this:
uiDiv.innerHTML = ""
// First iteration of for-loop
uiDiv.innerHTML === <button1 onclick="...">
// Second iteration of for-loop
// 'onclick' handler from button1 disappeared
uiDiv.innerHTML === <button1> <button2 onclick="...">
// Third iteration of for-loop
// 'onclick' handler from button2 disappeared
uiDiv.innerHTML === <button1> <button2> <button3 onclick="...">
you can write
onclick = 'myFunction(this.id):'
in the button then later:
function myFunction(idNum){console.log("Button No."+idNum);}
also here are some improvements you may want to implement:
uiDiv.innerHTML = uiDiv.innerHTML + ....;
uiDiv.innerHTML += ....;
you also want to declare "uiDivContent" before the loop then afterward write:
uiDiv.innerHTML = uiDivContent;
The event handler is bound to n, which has the value results.length by the time the event fires.
You have to close the value of n by making a copy, you can do this by calling a function. This construction is known as a closure.
for(var n=0;n<results.length;n++)
{
uiDiv.innerHTML = uiDiv.innerHTML + '<button id="connect'+n+'">option'+n+':'+results[n]+'</button><br>';
var tempId = document.getElementById('connect'+n);
tempId.addEventListener('click', (function(bound_n) {
console.log("Button No."+ bound_n);
})(n)), false); // <-- immediate invocation
}
If n were an object, this wouldn't work because objects (unlike scalars) get passed-by-reference.
A great way to avoid having to write closures, in all your for-loops, is to not use for-loops but a map function with a callback. In that case your callback is your closure so you get the expected behaviour for free:
function array_map(array, func) {
var target = [], index, clone, len;
clone = array.concat([]); // avoid issues with delete while iterate
len = clone.length;
for (index = 0; index < len; index += 1) {
target.push(func(clone[index], index));
}
return target;
}
array_map(results, function (value, n) {
uiDiv.innerHTML = uiDiv.innerHTML
+ '<button id="connect'+n+'">option'+n+':'+results[n]+'</button><br>';
var tempId = document.getElementById('connect'+n);
tempId.addEventListener('click', function(){console.log("Button No."+n)}, false);
});

javascript - basic onclick event question

I have a dynamic table populated from an array.
When building the table I have the following inside of a loop:
var tdRecord = trRecord.insertCell(trRow.cells.length);
var tdRecordId = dataArray[j][0];
tdRecord.onclick = function() { alert(tdRecordId); }
The problem is that alert will only alert the last set tdRecordId in the array. If I click on any of the other td rows they all alert the same number.
Anyone know how I can fix this?
This should work:
(function( id ) {
tdRecord.onclick = function() {
alert( id );
};
}( tdRecordID ));
You seem to be running your code inside a loop. In that case, all click handlers will point to the same tdRecordId value. If you want to capture the value of the current iteration, you have to use a function wrapper which will do that for you.
tdRecord.onclick = function () { alert('123'); };
You could use jQuery's data feature: http://jsfiddle.net/zRXS6/.
$(function(){
var number = 1;
var div1 = $('<div>a</div>');
div1.data('number', number);
div1.click(function() {window.alert($(this).data('number'))});
number = 2;
var div2 = $('<div>b</div>');
div2.data('number', number);
div2.click(function() {window.alert($(this).data('number'))});
$('body').append(div1).append(div2);
});
tdRecord.onclick = "alert(" + tdRecordId + ")";
Set it as a literal, rather then a dynamic. :)
In what you're doing, it will always refer to the variable, rather then the current value.
So as the variable changes, what the function alerts will change.
In this, it actually inserts the value, rather then the variable itself, so it will stay the same instead of changing.

Categories

Resources