setTimeout in a for loop with array as an argument misbehaving - javascript

I have been working on my second game, which works as follows:
There is a button and its position changes randomly every x seconds
The x seconds is controlled by setTimeout
Whenever the user clicks on the button their score increments by 10
When the score reaches 30 startGame calls Initialize, which creates a new button using JavaScript and pushes the button to an array called numOfBox
The control passes back to startGame with the updated numOfBox, which now has two buttons and changes the top/left coordinates every x seconds
The function is being called by setTimeout multiple times every x seconds instead of once every x seconds. How do I call the function only once every x seconds?
var box=document.getElementById('boxId');
var main=document.getElementById('Maincont');
var timeout=[];
var cnt=0;
var scr=0;
var levelCnt=30;
var divcnt=0;
var numOfbox=[];
var fcnt=0;
function createDiv(Name,Width,Height,Background,Margin,Padding) {
var t=t+divcnt;
divcnt+=1;
var Name=Name+divcnt;
var Nameid='boxId'+divcnt;
Name=document.createElement('button');
Name.id=Nameid;
console.log('IN CREATEDIV :-'+ Nameid+':::'+Name+' cnt '+ cnt);
Name.style.width=Width;
Name.style.height=Height;
Name.style.background=Background;
Name.style.margin=Margin;
Name.style.padding=Padding;
Name.style.boxSizing='border-box';
Name.style.position='absolute';
Name.style.top='10px';
Name.style.left='10px';
Name.style.color='white';
Name.style.textAlign='center';
Name.style.fontSize='15px';
Name.style.textDecoration='none';
Name.style.cursor='pointer';
Name.disabled=true;
t=document.createTextNode('HIT ME');
Name.appendChild(t);
Name.addEventListener('click', function() {
this.style.background='black';
scr+=10;
cnt+=10;
this.innerHTML= 'SCORE = ' +String(scr);
});
var b=document.getElementById('Maincont');
// FIRST HIT ME BOX IS READY NOW....
b.appendChild(Name);
// Array numOfbox loaded with all the Box id's.
numOfbox.push(Name.id);
console.log('PUSHING DIV:-'+ Name.id + " IN ARRAY :=" +numOfbox);
// creatediv is called when score is scr%30==0.
if (numOfbox.length > 1) {
return Name.id;
}
}
function Initialize() {
if (main.childNodes.length < 5) {
if ((cnt > 0) && (cnt %30 ==0)) {
var id='targetCont'+divcnt;
divcnt+=1;
boxId=createDiv(id,'130px','50px','black','0px','0px');
console.log('Inside Initialize:-'+id+' cnt '+ cnt);
cnt=0;
}
startGame();
}
}
function startGame() {
var d=new Date();
var t=d.getMinutes()+':'+d.getSeconds();
fcnt+=1;
console.log('TIME IS:-'+ t + " cnt:-" +fcnt+' INSIDE STARTGAME:-'+numOfbox+':'+numOfbox.length+ ' CNT'+cnt);
for (var i=0 ; i < numOfbox.length ; i++) {
if ((cnt > 0) && (cnt %30 ==0)) {
Initialize();
} else {
console.log('STARTING GAME FOR DIV:='+numOfbox[i]);
var box=document.getElementById(numOfbox[i]);
console.log(box);
box.disabled=false;
var max=500;
var min=10;
var topRand=(Math.floor(Math.random() * (max-min+1) + min));
var max=1200;
var min=10;
var leftRand=(Math.floor(Math.random() * (max-min+1) + min));
box.style.background='black';
box.style.top=topRand+'px';
box.style.left=leftRand+'px';
console.log('CNT:='+cnt);
timeout=setTimeout(startGame,10000,numOfbox);
}
}
}
function stopGame() {
clearTimeout(timeout);
console.log('IN STOPGAME:-'+timeout+' length '+timeout.length);
for (var i=0 ; i < numOfbox.length ; i++) {
console.log(timeout[i]);
clearTimeout(timeout[i]);
var box=document.getElementById(numOfbox[i]);
box.style.background='red';
box.style.top='10px';
box.style.left='10px';
}
timeout=[];
}
I would prefer to do this in JavaScript and not in ECMA5/6 or jQuery, or using arrow functions. I can provide the rest of the code if needed.

I noticed a couple of issues with your code.
You are calling setTimeout in a loop. So if you have, say, numOfbox.length === 15, setTimeout will be called 15 times around 10,000 ms from when you set it. Could this be the reason you're seeing the more calls to startGame than you thought?
I see the variable numOfbox, but since it's not declared in the startGame function I'll have to assume that it's in the parent scope. So, in the line where you do
timeout[i] = setTimeout(startGame, 10000, numOfbox);
Realize that since numOfbox is in a higher level scope and startGame does not take any parameters, the numOfbox parameter (the 3rd argument in setTimeout) is really not going anywhere: the numOfbox variable actually uses is coming from the parent scope. This may be ok, but you should consider what is happening here.

Related

Preserve variable's value for $http.get async result processing

Let us consider that we have a function, invoked on some event (let's say - scroll event). This function loads more items into some list.
Suppose that the logic of this function is designed as follows:
function() {
oldSize = list.length;
// add new items (prepare them in advance)
for (i = 0; i < PAGE_SIZE; i++) list.push({});
$http.get("/next/page/" + oldSize).then(function() {
// here I want operate with oldSize value which is actual on moment of
// the $http.get invocation:
for (i = 0; i < PAGE_SIZE;i++) {
// do something with
list[oldSize + i] = ... ;
}
}
}
The problem is that the entire function can be invoked almost simultaneously multiple times, what leads to the effect, that .then(function() { operates with improper value of oldSize variable - it becomes the value of last list.length, while I need it be preserved as it was on the moment of invocation.
For instance, if this event listener was invoked almost simultaneously 2 times, it will be:
oldSize == 5, list increased by 10 (for example) elements. But inside $http.get(...).then() I need to work with value oldSize == 5.
Second invocation: oldSize == 15 (because we have increased list by 10 elements in the first invocation). So inside this particular $http.get(...).then() I want to have oldSize == 15.
I hope it is clear. Please, do not suggest me to change my logic. I just want to know how to save variable value for postponed result of the asynchronous function (in my case it is $http.get(...).then(...)). Thanks.
Assuming you are not able to define oldSize inside this function because you need it somewhere else.
function() {
oldSize = list.length;
// add new items (prepare them in advance)
for (i = 0; i < PAGE_SIZE; i++) list.push({});
var currentOldSize = oldSize;
$http.get("/next/page/" + oldSize).then(function() {
// here I want operate with oldSize value which is actual on moment of
// the $http.get invocation:
for (i = 0; i < PAGE_SIZE;i++) {
// do something with
list[currentOldSize + i] = ... ;
}
}
}
Why is oldSize declared outside the scope or globally? Declare the variable in the scope of the function.
let list = [];
function() {
let size = list.length;
$http.get(...)
.then(function() {
// handle size
[...]
});
};

setTimeout within 'while' causes a browser crash. How can I avoid it?

I am new to coding and I want to built a Text Adeventure Game with HTML , CSS and Javascript. I want to show many peaces of text from an array with a certain time between each text. I tried different methods to avoid an infinite loop because of setTimeout, but I didn't figure out how to apply it on my code.
Here's my code, that causes the crash:
var iCounterText = 0;
var verzog = function() {
document.getElementById('toggleText').insertAdjacentHTML('beforeBegin', '<br>--------------<br>');
document.getElementById('toggleText').insertAdjacentHTML('beforeBegin', part1[iCounterText]);
iCounterText = iCounterText + 1;
playaudio();
}
function forwardingLinks() {
while (iCounterText < part1.length - 1) {
setTimeout(verzog, 500); // Here is the problem //
}
document.getElementById('buttonLinks').innerHTML = part1[part1.length - 1];
}
The following code works fine, but then there is no timeout between the text:
function forwardingLinks() {
while (iCounterText < part1.length - 1) {
verzog();
}
document.getElementById('buttonLinks').innerHTML = part1[part1.length - 1];
}
Edit:
This is my new code with "setInterval". Problem: Value is added by 1, but the function is not using the part2 Array. Instead it is using part1 Array again, although the partvalue already contains part2 array.
var part1 = [ //Texte und Antworten
'Hallo?',
'Test?',
'What',
'hello',
'--------------',
'Was?'
];
var part2 = [ //Texte und Antworten
'part2 goes on....',
'bla bla',
'blablabla'
];
var iCounterText = 0;
var value = 1;
var partvalue = eval("part" + value);
function forwardingLinks() {
var verzog = setInterval(function(){
if(iCounterText < partvalue.length-2){
++iCounterText;
toggleText.insertAdjacentHTML('beforeBegin', '<br>--------------<br>');
toggleText.insertAdjacentHTML('beforeBegin', partvalue[iCounterText]);
playaudio();
}else{
buttonLinks.innerHTML = partvalue[partvalue.length-1];
++value;
iCounterText=0;
clearInterval(verzog);
}
},500);
}
You are misunderstanding the flow of your while loop. JavaScript runs in a single-threaded environment. That means that your calls to the verzog function won't run until the forwardingLinks function completes, but your forwardingLinks function won't ever complete because you have a while loop that is dependent on a counter that is never increased, because verzog hasn't run yet.
Change your while loop, so that the iCounterText variable gets incremented from within the loop, so the loop can end and then the calls to verzog that have stacked up in the event queue can start to run.
Additionally, since you are using a numeric counter, a regular for loop would be better than an while loop because the loop's step value (++iCounterText) is required:
for(var iCounterText = 0; iCounterText < part1.length; ++iCounterText) {
setTimeout(verzog, 500);
}
As an aside from your main problem, it is very inefficient to repeatedly scan the DOM for the same element over and over, as you are doing in your verzog function. Instead, just get the DOM reference once and store it in a variable that can be reused:
// Declare a variable in a scope that is accessible throughout your code
var toggleText = null, buttonLinks = null;
// Set up a callback that runs after the DOM is ready
window.addEventListener("DOMContentLoaded", function(){
// Scan the DOM for the element(s) you'll be needing
toggleText = document.getElementById('toggleText');
buttonLinks = document.getElementById('buttonLinks');
});
function verzog() {
// Now, you can just refer to the DOM element you've already found:
toggleText .insertAdjacentHTML('beforeBegin', '<br>--------------<br>');
toggleText .insertAdjacentHTML('beforeBegin', part1[iCounterText]);
playaudio();
}
function forwardingLinks() {
for(var iCounterText = 0; iCounterText < part1.length; ++iCounterText) {
setTimeout(verzog, 500);
}
// Now, you can just refer to the DOM element you've already found:
buttonLinks.innerHTML = part1[part1.length - 1];
}

Int from for loop not working in function - JS

I am pretty new to Javascript and have noticed this odd issue come up.
var dispatchMouseEvent = function(target, var_args) {
var e = document.createEvent("MouseEvents");
e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
};
var Level1Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry "); //GETTING LEVEL 1 CATS
var Level1CatsLen = Level1Cats.length; //GETTING LEVEL 1 CAT LEN
for (i = 0; i <= Level1CatsLen-1; i++) {
var ID1 = Level1Cats[i].id;
var temp1 = Level1Cats[i].innerHTML;
temp1.replace(/&/gi, "&").replace(/<[^>]*>/gi, "");
function GoToLevel2(callback) { //GO TO NEXT LEVEL!
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
}
function GetLevel2() { //GET NEXT LEVEL
var Level2Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry");
return Level2Cats.length;
}
setTimeout(function() { //RUN IT WITH TIMING
GoToLevel2();
}, 100);
var Level2CatsLen = GetLevel2();
}
When the code is executed it gives me an error (Cannot read property 'dispatchEvent' of undefined)
I know this is because the i in the function does not seem to work. If I simply replace it with an int value of 1 it will execute and click cat 1, 16 times, as expected..
I would have thought this should work, any ideas how I can work around it?
Inside the loop, a closure GoToLevel2 is created, closing over the variable i, when i is 1. Then the loop runs through, i is incremented to 2, and the loop is terminated.
Then your setTimeout fires after 100ms, and invokes your closure. It still remembers that there was once a variable i, but it now contains 2. Level1Cats[2] is undefined, and you get an error.
The standard solution is to enclose the contents of the loop into another self-evaluating function that will not close over i:
for (i = 0; i <= Level1CatsLen-1; i++) {
(function(i) {
// ...
})(i);
}
(Note also that setTimeout(function() { GoToLevel2(); }, 200) is identical to, but less efficient than setTimeout(GoToLevel2, 200).)

I keep receiving Uncaught TypeError: Object [object global] has no method error during function call

I created a function that would do something unique on it's 3rd call. In this case, alert the incremented number. However, when trying to call this function from another function:
"MyFunction();", I get Uncaught TypeError: Object [object global] has no method 'MyFunction'. Can you please tell me what I'm doing wrong?
var counter = 0;
var num = 0;
function = MyFunction() {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}
I've also tried removing the = sign, as you can see here http://jsfiddle.net/DN3yC/6/ it doesn't work.
LIVE DEMO
Just remove the = sign function MyFunction() { and make sure in your fiddle that JS <script> is in the right placeSee additional explanation below.
Example:
var element = document.getElementById('button');
var counter = 0;
var num = 0;
function MyFunction() {
counter = ++counter % 3; // loop count
if ( !counter ) {
num++;
alert('the new number is: ' + num);
}
}
//On click:
element.addEventListener('click', MyFunction, false);
your new fiddle: http://jsfiddle.net/DN3yC/7
The old one of yours was not working cause you did not used No Wrap - in body for the JS in your fiddle. (See options panel at the left up corner)
So basically you need to put your <script> tags before your </body> closing tag in order to make sure the DOM is ready and parsed by JavaScript before elements manipulation.
Syntacs looks wrong to me, try
var foo = Myfuntion();
or
var foo = new MyFuntion();
depending on what MyFuntion() is.
It should be:
var MyFunction=function () {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}

jscript/jquery: fade in divs 1 by 1, with pause inbetween

I am totally stumped here.
I have a bunch of messages, each within a div "msg_feed_frame_$i" which increases, so message 1 is in div "msg_feed_frame_1", msg 2 is in "msg_feed_frame_2", etc etc. I want to fade these messages in one by one using jquery, but for some reason no matter what I do with settimeout and setinterval, the code immediately fades them all in simultaneously.
This is what i've got so far:
function feed(i)
{
var div = "#msg_feed_frame_";
var fader = div.concat(i);
$(fader).fadeIn(2000);
}
function timefade()
{
var num = 1;
for (num=1; num<=10; num++)
{
var t=setTimeout(feed(num),1000);
}
}
$(document).ready(function(){
timefade();
});
Your problem is that the for loop executes quickly and sets a timeout 1000ms from now for each function call. Here's one way you could remedy this:
function feed(i) {
var div = "#msg_feed_frame_";
var fader = div.concat(i);
$(fader).fadeIn(2000);
}
function timefade() {
var num = 1;
var fadeIt = function(i) {
return function() { feed(i); };
};
for (num = 1; num <= 4; num++) {
var t = setTimeout(fadeIt(num), num * 1000);
}
}
$(document).ready(function() {
timefade();
});
Additionally, your original code was passing setTimeout the results of the feed(i) function call (undefined), and it expects an object of type function. I added a helper function that returns a reference to a function that will call feed with the correct argument.
Capturing the value of num inside a function is called a closure, and it can be confusing at first. Check this post out for some good explanations of closures inside loops and while they're necessary.
Here's your example, working: http://jsfiddle.net/andrewwhitaker/tpGXt/
Try using jQuery's .delay() function...
function feed(i){
var div = "#msg_feed_frame_";
var fader = div.concat(i);
$(fader).fadeIn(2000).delay(1000);
}
function timefade(){
var num = 1;
for (num=1; num<=10; num++){
feed(num);
}
}
$(document).ready(function(){
timefade();
});

Categories

Resources