JavaScript - Function Not Modifying Global Variable - javascript

I'm having trouble modifying a variable within a text adventure game I'm writing code for in JavaScript. I want to change the value of a global variable using a function within a function (no, not closure).
/*This is just example code.*/
var health = 100;
var exp = 0;
function refreshStats() {
health -= 10;
exp += 1;
}
function foo(flag) {
if (flag == DONOTHING) {
refreshStats();
}
if (health <= 0) {
say("You died, bwahaha.");
}
if ((exp/10) == Math.floor(exp/10)) {
health += 10;
say("You leveled up!");
}
}
How the game works is that each function (defined as actions or areas within the game) will be called by buttons and forms placed by JavaScript that the user can click or write in, respectively. I need refreshStats() to update the health and exp variables so foo() can use them correctly; the function doesn't seem to be updating the variables until after foo() runs. I do wonder if it's a browser compatibility issue, which really would tick me off, but I'm hoping it isn't that.
Any suggestions?

Store your values into your window object, so it will be available in any scope of that window:
//take care to not overwrite native properties of the window
window.health = 100;
window.exp = 0;
function refreshStats() {
health -= 10;
exp += 1;
}

It looks like it's working for me. Perhaps it's a scoping issue - instead of example code, could you post your actual relevant code?

Related

How to access a variable value that is defined inside another function JS

I have a variable totalActivatedOffers which is defined inside a function, inside an IF statement. I need to be able to use the value of this variable outside of the IF statement by another function but can't get it to work, it keeps returning undefined.
I've declared it at the top of the script block like this:
var totalActivatedOffers;
But still no joy. Here is an example of how the value is assigned to it (simplified for illustration):
if ("SSO" in sessionStorage) {
setTimeout(function(){
if (firstArrayLength.length == 3) {
var totalActivatedSparksOffers = 4;
}
if (secondArrayLength.length == 3) {
var totalActivatedRewardsOffers = 7;
}
var totalActivatedOffers = totalActivatedSparksOffers + totalActivatedRewardsOffers;
}, 2500)
} // closes if SSO is inside session storage
// need to access the value here, outside of the IF statement
console.log('Total activated offers to subtract is ' + totalActivatedOffers);
Any help would be greatly appreciated.
SOLUTION:
Managed to get around the issue by making it a window variable like this:
window.totalActivatedOffers = totalActivatedSparksOffers + totalActivatedRewardsOffers;
Managed to solve the issue by making it a window variable:
window.totalActivatedOffers = totalActivatedSparksOffers + totalActivatedRewardsOffers;

How to make private or local variables?

So I checked a thread on here about global and local variables but didn't really find a solution to my problem. I just want a private or local variable to increment so that a function only fires once. I'll paste what I'm trying to achieve here any help would be much appreciate also please go easy on me I'm brand new to JavaScript. This code works but the variable I seems to be shared between functions.
function phonefun(){
i++;
console.log(i);
wage = wage - phone;
console.log(wage);
display();
document.getElementById('phone').style.backgroundColor = "darkgrey";
}
function waterfun(){
i++;
console.log(i);
wage = wage - water;
console.log(wage);
display();
document.getElementById('water-aid').style.backgroundColor = "darkgrey";
}
...the function is called on the click of a button, I want it so you
can only press the button
I think what you want to do is have your event handler unbind from the button after if fires. Thas is much better solution than counting how many times it's been clicked. Check out this link for how to bind and unbind event handlers using "vanilla" JS: https://plainjs.com/javascript/events/binding-and-unbinding-of-event-handlers-12/
In reference to your earlier questions...
A variable created inside of a function is said to be "scoped" to that function, which means that nothing outside of that function can access the variable. However, by initializing your variable without using the var or let keyword (the latter is ES6 syntax), you created an implicit global. This means that you inadvertently made it a global variable when you wanted it to be function-scoped.
Declaring a variable does not automatically assign a value of zero. If you do not assign a value, the value will be undefined.
If you had declared / assigned the variable thusly,var i = 0; or let i = 0; you would have had a properly scoped variable with an initial value of 0. The problem is, each time that function executed, the value would be reset to zero. To get the value to "stick" you would have to create state. You could do that by creating an object with getter and setter methods or by using a closure. However, the unbind solution seems to be the best way to go for what you want to do here.
I hope this helps.
To do what you want, you need a variable with a higher scope than the function so that the value can persist between function calls. A local variable will be garbage collected as the function returns and so, your counter would be lost.
var counter = 0; // This variable exists in a higher scope than the function
function loanfun(){
counter++;
if (counter == 1) {
console.log("function has run " + counter + " times.");
}
}
loanfun(); // Will run
loanfun(); // Won't run
you can make a class
function myStuff(){
this.i = 0,
this.loanfun = function(){
this.i++;
if (this.i == 1) {
wage = wage - loan;
console.log(wage);
display();
document.getElementById('loan').style.backgroundColor = "darkgrey";
}
}
}
var s = new myStuff();
s.loanfun();
s.loanfun();
You could try namespacing within an object:
var PageModule = {
count: 0,
loadfun: function (wage, loan) {
PageModule.count += 1;
if (PageModule.count === 1) {
console.log('execute!');
wage = wage - loan;
console.log(wage);
display();
document.getElementById('loan').style.backgroundColor = "darkgrey";
}
}
};
PageModule.loadfun();
PageModule.loadfun();
PageModule.loadfun();
// if you want to attach the method to a button
document.getElementById('my-btn-id').addEventListener('click', PageModule.loadfun);
Alternatively, you could use the following approach:
function myclickhandler () {
// do whatever you want here ...
//remove handler from button, so that the next button clicks will not do anything
document.getElementById('my-btn-id').removeEventListener('click', myclickhandler);
}
// attach the method to a button
document.getElementById('my-btn-id').addEventListener('click', myclickhandler);
Hope that this is what you want to do.But if you want simply to call(invoke) you function once just call and it will be executed only one time.
wage = 10;
loan = 5;
i=0; //this is the global variable
function loanfun(){
let j = i +1; //j is local variable
if (j === 1) {
wage = wage - loan;
console.log(wage);
//display();
document.getElementById('loan').style.backgroundColor = "darkgrey";
}
}
loanfun(); //invoke the function here
<div id="loan">
hi I am here working as expected
</div>

Static javascript variable to be used as counter in Angularjs controller

I would like to create a static javascript variable to be used as a counter inside a Angularjs controller. This static variable will be used inside a polling function that gets repeatedly called.
I want to use the static variable in a manner that looks like this;
var polling_func = function()
{
static var counter = 0;
if (counter == 10)
{
alert('Do action');
counter = 0;
}
counter = counter + 1;
$timeout(polling_func, 1000);
}
polling_func();
Unfortunately, I cannot declare a static variable using static keyword in javascript. How should I go about doing so in my code?
I think #Naeem-Shaikh's answer is the simplest one, and pure JS.
But since you flagged angular, there is a more Angular-ish way to do it: use a service.
app.factory('Counter',function() {
return {c:0};
});
and then in your controller (or multiple controllers):
app.controller('MyCtrl',function(Counter) {
Counter.counter++;
});
factories/services are intended to be long-lived and pass methods and variables around between short-lived controllers.
If all you need is a var (i.e. no methods) like here, there is a short-hand:
app.value('Counter',{counter:0});
And then use it in controllers in the same way.
Why not declare a global variable, so it will not change the value whenever function is called.
var counter = 0;
var polling_func = function()
{
if (counter == 10)
{
alert('Do action');
counter = 0;
}
counter = counter + 1;
}
polling_func();
$timeout(polling_func, 1000);

Understanding JavaScript setTimeout and setInterval

I need a bit of help understanding and learning how to control these functions to do what I intend for them to do
So basically I'm coming from a Java background and diving into JavaScript with a "Pong game" project. I have managed to get the game running with setInteval calling my main game loop every 20ms, so that's all ok. However I'm trying to implement a "countdown-to-begin-round" type of feature that basically makes a hidden div visible between rounds, sets it's innerHTML = "3" // then "2" then "1" then "GO!".
I initially attempted to do this by putting setTimeout in a 4-iteration for-loop (3,2,1,go) but always only displayed the last iteration. I tried tinkering for a bit but I keep coming back to the feeling that I'm missing a fundamental concept about how the control flows.
I'll post the relevant code from my program, and my question would be basically how is it that I'm writing my code wrong, and what do I need to know about setTimeout and setInterval to be able to fix it up to execute the way I intend it to. I'm interested in learning how to understand and master these calls, so although code examples would be awesome to help me understand and are obviously not unwelcome, but I just want to make it clear that I'm NOT looking for you to just "fix my code". Also, please no jQuery.
The whole program would be a big wall of code, so I'll try to keep it trimmed and relevant:
//this function is called from the html via onclick="initGame();"
function initGame(){
usrScore = 0;
compScore = 0;
isInPlay = true;
//in code not shown here, these objects all have tracking variables
//(xPos, yPos, upperBound, etc) to update the CSS
board = new Board("board");
ball = new Ball("ball");
lPaddle = new LPaddle("lPaddle");
rPaddle = new RPaddle("rPaddle");
renderRate = setInterval(function(){play();}, 20);
}
.
function initNewRound(){
/*
* a bunch of code to reset the pieces and their tracking variables(xPos, etc)
*/
//make my hidden div pop into visibility to display countdown (in center of board)
count = document.getElementById("countdown");
count.style.visibility = "visible";
//*****!!!! Here's my issue !!!!*****//
//somehow i ends up as -1 and that's what is displayed on screen
//nothing else gets displayed except -1
for(var i = 3; i >= 0; i--){
setInterval(function(){transition(i);}, 1000);
}
}
.
//takes initNewRound() for-loop var i and is intended to display 3, 2, 1, GO!
function transition(i){
count.innerHTML = (i === 0) ? "Go" : i;
}
.
//and lastly my main game loop "play()" just for context
function play(){
if(usrScore < 5 && compScore < 5){
isInPlay = true;
checkCollision();
moveBall();
moveRPaddle();
if(goalScored()){
isInPlay = false;
initNewRound();
}
}
}
Thanks a bunch for your advise, I'm pretty new to JavaScript so I really appreciate it.
Expanding on cookie monster's comment, when you use setInterval in a loop, you are queueing up method executions that will run after the base code flow has completed. Rather than queue up multiple setInterval executions, you can queue up a single execution and use a variable closure or global counter to track the current count. In the example below, I used a global variable:
var i = 3 // global counter;
var counterInterval = null; // this will be the id of the interval so we can stop it
function initNewRound() {
// do reset stuff
counterInterval = setInterval(function () { transition() }, 1000); // set interval returns a ID number
}
// we don't need to worry about passing i, because it is global
function transition() {
if (i > 0) {
count.innerHTML = i;
}
else if (i === 0) {
count.innerHTML = "Go!";
}
else {
i = 4; // set it to 4, so we can do i-- as one line
clearInterval(counterInterval); // this stops execution of the interval; we have to specify the id, so you don't kill the main game loop
}
i--;
}
Here is a Fiddle Demo
The problem is in this code:
for(var i = 3; i >= 0; i--){
setInterval(function(){transition(i);}, 1000);
}
When the code runs, it creates a new function 3 times, once for each loop, and then passes that function to setInterval. Each of these new functions refers to the variable i.
When the first new function runs it first looks for a local variable (in it's own scope) called i. When it does not find it, it looks in the enclosing scope, and finds i has the value -1.
In Javascript, variables are lexically scoped; an inner function may access the variables defined in the scope enclosing it. This concept is also known as "closure". This is probably the most confusing aspect of the language to learn, but is incredibly powerful once you understand it.
There is no need to resort to global variables, as you can keep i safely inside the enclosing scope:
function initNewRound(){
var i = 3;
var count = document.getElementById("countdown");
count.style.visibility = "visible";
var interval = setInterval(function(){
//this function can see variables declared by the function that created it
count.innerHTML = i || "Go"; //another good trick
i-=1;
i || clearInterval(interval); //stop the interval when i is 0
},1000);
}
Each call to this function will create a new i, count and interval.

Write a wrapper object in Javascript

First off, let me apologize if my question isn't worded correctly - I'm not a professional coder so my terminology might be weird. I hope my code isn't too embarrassing :(
I have a fade() method that fades an image in and out with a mouse rollover. I would like to use a wrapper object (I think this is the correct term), to hold the image element and a few required properties, but I don't know how to accomplish this. fade() is called from the HTML, and is designed to be dropped into a page without much additional setup (so that I can easily add new fading images to any HTML), just like this:
<div id="obj" onmouseover="fade('obj', 1);" onmouseout="fade('obj', 0);">
The fade(obj, flag) method starts a SetInterval that fades the image in, and when the pointer is moved away, the interval is cleared and a new SetInterval is created to fade the image out. In order to save the opacity state, I've added a few properties to the object: obj.opacity, obj.upTimer, and obj.dnTimer.
Everything works okay, but I don't like the idea of adding properties to HTML elements, because it might lead to a future situation where some other method overwrites those properties. Ideally, I think there should be a wrapper object involved, but I don't know how to accomplish this cleanly without adding code to create the object when the page loads. If anyone has any suggestions, I would greatly appreciate it!
Here's my fader method:
var DELTA = 0.05;
function fade(id, flag) {
var element = document.getElementById(id);
var setCmd = "newOpacity('" + id + "', " + flag + ")";
if (!element.upTimer) {
element.upTimer = "";
element.dnTimer = "";
}
if (flag) {
clearInterval(element.dnTimer);
element.upTimer = window.setInterval(setCmd, 10);
} else {
clearInterval(element.upTimer);
element.dnTimer = window.setInterval(setCmd, 10);
}
}
function newOpacity(id, flag) {
var element = document.getElementById(id);
if (!element.opacity) {
element.opacity = 0;
element.modifier = DELTA;
}
if (flag) {
clearInterval(element.dnTimer)
element.opacity += element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity > 100) {
element.opacity = 100;
element.modifier = DELTA;
return;
}
element.opacity = Math.ceil(element.opacity);
} else {
clearInterval(element.upTimer)
element.opacity -= element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity < 0) {
element.opacity = 0;
element.modifier = DELTA;
return;
}
element.opacity =
Math.floor(element.opacity);
}
setStyle(id);
}
function setStyle(id) {
var opacity = document.getElementById(id).opacity;
with (document.getElementById(id)) {
style.opacity = (opacity / 100);
style.MozOpacity = (opacity / 100);
style.KhtmlOpacity = (opacity / 100);
style.filter = "alpha(opacity=" + opacity + ")";
}
}
You are right, adding the handlers in your HTML is not good. You also loose the possible to have several handlers for event attached to one object.
Unfortunately Microsoft goes its own way regarding attaching event handlers. But you should be able to write a small wrapper function to take care of that.
For the details, I suggest you read quirksmode.org - Advanced event registration models.
An example for W3C compatible browsers (which IE is not): Instead of adding your event handler in the HTML, get a reference to the element and call addEventListener:
var obj = document.getElementById('obj');
obj.addEventListener('mouseover', function(event) {
fade(event.currentTarget, 1);
}, false);
obj.addEventListener('mouseout', function(event) {
fade(event.currentTarget, 0);
}, false);
As you can see I'm passing directly a reference to the object, so in you fade method you already have a reference to the object.
You could wrap this in a function that accepts an ID (or reference) and every time you want to attach an event handler to a certain element, you can just pass the ID (or reference) to this function.
If you want to make your code reusable, I suggest to put everything into an object, like this:
var Fader = (function() {
var DELTA = 0.05;
function newOpacity() {}
function setStyle() {}
return {
fade: function(...) {...},
init: function(element) {
var that = this;
element.addEventListener('mouseover', function(event) {
that.fade(event.currentTarget, 1);
}, false);
element.addEventListener('mouseout', function(event) {
that.fade(event.currentTarget, 0);
}, false);
}
};
}())
Using an object to hold your functions reduces pollution of the global namespace.
Then you could call it with:
Fader.init(document.getElementById('obj'));
Explanation of the above code:
We have an immediate function (function(){...}()) which means, the function gets defined and executed (()) in one go. This function returns an object (return {...};, {..} is the object literal notation) which has the properties init and fade. Both properties hold functions that have access to all the variables defined inside the immediate function (they are closures). That means they can access newOpacity and setStyle which are not accessible from the outside. The returned object is assigned to the Fader variable.
This doesn't directly answer your question but you could use the jQuery library. It's simple, all you have to do is add a script tag at the top:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js">
Then your div would look like:
<div id="obj" onmouseover="$('#obj').fadeIn()" onmouseout="$('#obj').fadeOut()">
jQuery will handle all the browser dependencies for you so you don't have to worry about things like differences between firefox and mozilla etc...
If you want to keep your HTML clean, you should consider using JQuery to set up the events.
Your HTML will look like this:-
<div id="obj">
Your JavaScript will look "something" like this:-
$(document).ready(function() {
$("#obj").mouseover(function() {
Page.fade(this, 1);
}).mouseout(function(){
Page.fade(this, 0);
});
});
var Page = new function () {
// private-scoped variable
var DELTA = 0.05;
// public-scoped function
this.fade = function(divObj, flag) {
...
};
// private-scoped function
var newOpacity = function (divObj, flag) {
...
};
// private-scoped function
var setStyle = function (divObj) {
...
};
};
I introduced some scoping concept in your Javascript to ensure you are not going to have function overriding problems.

Categories

Resources