DOM changes don't execute in interval function - javascript

I'm working on a basic alerting system in javascript that makes a basic circle clock count down and then disappear on a map I'm making.
I have however run into a small snag since I can't figure out why line 11 and 12 in the code block below (marked by arrows) don't want to do their thing.
function createTimer(sysID){
this.IDstore = sysID;
this.time = 59;
var self = this;
document.getElementById('star'+self.IDstore).style.fill="#FF0000";
this.timer = setInterval(function(){
document.getElementById('Timer'+self.IDstore).setAttribute('d', updateState(self.time));
self.time-=1;
if(self.time<=0){
-> document.getElementById('Timer'+self.IDstore).style.visibility="hidden";
-> document.getElementById('star'+self.IDstore).style.fill="#FFFFFF";
clearInterval(self.timer);
}
},1000);
this.stopTime=stopTime;
}
If I however remove the clearInterval or move them out of the if statement they work fine. But simply making them run by themself before the execution of the clearInterval like in the next code block does not work.
function createTimer(sysID){
this.IDstore = sysID;
this.time = 59;
var self = this;
document.getElementById('star'+self.IDstore).style.fill="#FF0000";
this.timer = setInterval(function(){
if(self.time>=0){
document.getElementById('Timer'+self.IDstore).setAttribute('d', updateState(self.time));
}else if(self.time>-2){
document.getElementById('Timer'+self.IDstore).style.visibility="hidden";
document.getElementById('star'+self.IDstore).style.fill="#FFFFFF";
}else{
clearInterval(self.timer);
}
self.time-=1;
},1000);
this.stopTime=stopTime;
}
Would be great to know why and if there is a simple solution.

Related

Cant clearInterval and then trigger another setInterval

I have a list of users where clicking on each user I get a different timestamp from which I am showing a timer which is placed in a span with id #timer
<span id="timer"></span>
And this works as it is supposed to work.
Here is a jsfiddle.
jsfiddle
It seems that the clearInterval(x); doesn't stop the previous timer though, but start a new timer, and I get this (two timers at the same time)
I have tried also to use window.clearInterval(x); but it didnt work either. Also tried setting var x inside the $(document).on('click', '.list', function() as well as outside, but nothing works.
You have a shadowed x here:
var x;
$(document).on('click','#timer',function() {
var timestamp = localStorage.getItem("LivechatExpirationTime");
var countdown = new Date(timestamp).getTime();
//alert (countdown)
var x = setInterval(function () // <-- you redeclare x, so it's different from the first one
if you just remove the second var, it will work as expected:
var x;
$(document).on('click','#timer',function() {
var timestamp = localStorage.getItem("LivechatExpirationTime");
var countdown = new Date(timestamp).getTime();
//alert (countdown)
x = setInterval(function ()
https://jsfiddle.net/wosehfc3/

Angular timer custom countdown variable

I'm using angular-timer: http://siddii.github.io/angular-timer/
My goal is to create a timer for an app that keeps reference to a variable somewhere else. That way instead of having a timer that just restarts on page load I will have a timer that consistently counts down regardless of what the user does. Most examples with angular-timer have you enter a countdown number. Is there any way to pass in a variable like so:
var timeRemaining = 1000;
<h1 class="timer"><timer countdown=timeRemaining max-time-unit="'minute'" interval="1000">{{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}}</timer></h1>
Instead of being forced to write the countdown like this:
countdown="1000"
I've already tried passing in the variable via the toString() method as well. Thanks.
It looks like you cannot do what you are trying to without editing the directive itself.
Alternatively, you could use the timer from this question on your scope, just modify it to count down: https://stackoverflow.com/a/12050481/4322479
Counting down, from the question's comments: http://jsfiddle.net/dpeaep/LQGE2/1/
function AlbumCtrl($scope,$timeout) {
$scope.counter = 5;
$scope.onTimeout = function(){
$scope.counter--;
if ($scope.counter > 0) {
mytimeout = $timeout($scope.onTimeout,1000);
}
else {
alert("Time is up!");
}
}
var mytimeout = $timeout($scope.onTimeout,1000);
$scope.reset= function(){
$scope.counter = 5;
mytimeout = $timeout($scope.onTimeout,1000);
}
}
No need to edit directive, you could also use ng-if on your timer element to check for your startTime variable.
<timer ng-if="yourCountdownVaariable>0" countdown="yourCountdownVaariable"
max-time-unit="'hour'" interval="60000">
{{hhours}} hour{{hoursS}}, {{mminutes}} minute{{minutesS}}
</timer>
This way, the directive will only be initialised when you have your date, and it will work. Original issue below:
https://github.com/siddii/angular-timer/issues/36

Interval between js keypress events and action if keypress event is not occurring

In my code below, different words are shown in a <div> on a keypress event or after 1500ms if no key is pressed. The elapsed time between the appearance of the word and the keypress is my reaction time and it's saved in the variable reac.
This all works fine. But now I'd like to have two adjustments:
The reaction time should be equal to 1500 if no key is pressed. As it is now, the time runs through until a key is pressed.
I want an Interval of 500ms between the disappearance of an old word and the appearance of the new one.
I assume it's setTimeout or setInterval, yet I tried and it never worked out perfectly.
Here's my script (I shortened it to make it more readable, so it is possible that I forgot to close a bracket in the example below - hope not though):
$(document).ready(function(){
var upda = function() {
(show random word in div)
};
t1 = (new Date()).getTime();
timer = setInterval(upda, 1500);
$(document).keypress(function(e){
clearInterval(timer);
var t2 = (new Date()).getTime();
reac = t2 - t1;
t1 = t2;
if (e.keyCode == 97) {
(show another random word in div)
};
timer = setInterval(upda, 1500);
});
});
You don't really want an interval, you want a timeout
The general idea is that you set an expiration of 1500ms; if the user hasn't provided the appropriate input by the expiration of that input, your timeout expires and the timeout function fires, setting the default reac value and restarting your timer.
The keypress handler would then short-circuit the expiration and record the "actual" reac.
As a side note, you probably realize that browser-based JavaScript is a bad choice for any kind of sensitive timing operations, so we'll just go ahead and assume this is for a use case where truly accurate timing data isn't of critical importance. :)
EDIT
As an exercise, I reworked the code to use timers instead of intervals, and to separate tasks into individual functions. This is only one example; other developers may take different approaches. For example, in a larger project, this would almost certainly be encapsulated in an object library that you could reuse around the application.
var expectedInput, inputTimer, reac, startTime;
var $document = $(document);
var defaultReacTime = 1500;
var delayBetweenInputs = 500;
var timerInterval = 1500;
var showWordAndWaitForInput = function () {
startTime = (new Date()).getTime();
$document.on('keypress', keypressHandler);
expectedInput = 97;
console.log('Waiting for <expectedInput> at <startTime> ::', expectedInput, startTime);
inputTimer = setTimeout(timerExpires, timerInterval);
};
var stopWaitingForInput = function () {
clearTimeout(inputTimer);
$document.off('keypress', keypressHandler);
};
var recordReacAndResetTimer = function (reactionTime) {
reac = reactionTime;
console.log('reac ::', reac);
setTimeout(showWordAndWaitForInput, delayBetweenInputs);
};
var timerExpires = function () {
stopWaitingForInput();
console.log('timer expired');
recordReacAndResetTimer(defaultReacTime);
};
var isInputValid = function (e) {
return e.keyCode === expectedInput;
};
var keypressHandler = function (e) {
console.log('input received ::', e.keyCode);
if (isInputValid(e)) {
console.log('input is valid, ask for new input');
stopWaitingForInput();
var endTime = (new Date()).getTime();
recordReacAndResetTimer(endTime - startTime);
} else {
console.log('input is invalid, keep waiting');
}
};
setTimeout(showWordAndWaitForInput, delayBetweenInputs);
Hope this helps.

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.

stopping dynamically generated setInterval

I am generating multiple charts each with their own setInterval to refresh the data. I have it set to clearInterval when the dynamically generated container is removed - but if I reload and it has the same id the old setInterval continues to run. Is there a way to set a dynamically named setInterval that can be stopped when the replacement is generated?
Right now I'm using:
function generateChart(data, location){
var chart = new Highcharts.Chart({
// blah blah blah
}, function(chart){
setInterval(function(){
if($('#'+location).length){
// I'm doing stuff every minute
}else{
clearInterval();
}
},60000);
});
}
What happens is, the location is a randomly generated string that becomes the element ID for the container for the Highchart and if they user saves the chart it becomes the unique identifier. If the user updates the chart that's saved and reloads the chart, the old one gets .removed() and the new one generated in its place. Now the new one has the same element ID as the old one and since the old interval finds the container it wants it attempts to continue updating - which is can't since its chart went poof.
is there a way to set a dynamic variable I can use for setInterval so that I can clearInterval on it?
var blob+location = setInterval(function(){ ...
and then
clearInterval(blob+location);
You can just use an object:
var myObj = {};
var location = "somevalue";
myObj[location] = setInterval(...
clearInterval(myObj[location]);
ok - since I couldn't seem to wrap my head around some of your answers I decided to go low tech.
function genToken(){
var num = Math.floor(Math.random() * 10000);
var token = 't-' + num;
return token;
}
function genLocation(){
var chartToken = genToken();
var newChart = '<div id="'+location+'" data-token="'+chartToken+'"></div>';
$('#chartHome').append(newChart);
}
// inside my chart function
var token = $('#'+location).data('token');
setInterval(function(){
if( $('[data-token="'+token+'"]').length ){
// still there - keep going
}else{
// all gone - time to stop
clearInterval();
}
},60000);
now when I do:
$('#'+location).remove();
the token also vanishes and won't be the same if I generate a new chart with the same location id.
Stop using setInterval, use setTimeout instead (How do I execute a piece of code no more than every X minutes?):
function generateChart(data, location) {
var element = $('#'+location);
var chart = new Highcharts.Chart({
// blah blah blah
}, foo);
var foo = function() {
if(element){
// I'm doing stuff every minute
setTimeout(foo, 6000);
}
};
}
To stop it, just avoid the setTimeout or make element = null.
Maybe my code is a little bit wrong (I'm getting sleep right now), but the thing is to use setTimeout and closures.
If inside foo, something longs more than 6 seconds you will be in troubles since setTimeinterval will call it again, please watch http://www.youtube.com/watch?feature=player_detailpage&v=i_qE1iAmjFg#t=462s , so, this way you ensure that this will run 6 seconds after the last completed stuff.
I'll let this example here to posterity:
http://jsfiddle.net/coma/vECyv/2/
var closure = function(id) {
var n = 0;
var go = true;
$('#' + id).one('click', function(event) {
go = false;
});
var foo = function() {
if(go) {
console.log(id, n++);
setTimeout(foo, 1000);
}
};
foo();
};
closure('a');
closure('b');
Not sure if anyone is still looking for this solution but I ran into this problem and chose the following approach.
For anyone dynamically creating private/anonymous intervals that need to be stopped based on some event. You can simply save the interval in a variable, then transfer that variable into a data property in your html element.
// Outer scope
let pos = 1
let interval = setInterval(() => {
if (pos < 700) {
pos++;
}
htmlEl.style.top = pos + "px";
});
htmlEl.setAttribute("data-interval", interval)
This will save the numeric identifier of your interval, providing that html element is somewhere in your DOM.
Then, later you can simply extract this data attribute and use it to cancel an interval.
let intervalId = document.querySelector("#someElement").dataset.interval;
clearInterval(intervalId);

Categories

Resources