JavaScript clearTimeout not firing correctly - javascript

I have a submit function on a textbox with JavaScript. When the script fires, it checks a Kendo grid for a certain article and adds +1 to its quantity as well as opening the corresponding cell in editing mode. What I want to achieve is that on every submit the timer that starts grid.editCell() will reset its timer.
Currently, the event fires properly. However, the timer doesn't get reset, although the clearTimeout() does work if I just simply start the timer and then clear it right afterwards.
JavaScript:
$('#txtBarcode').submit(function (e) {
var grid = $("#PickListDetailGrid").data("kendoGrid");
var dataSource = $("#PickListDetailGrid").data("kendoGrid").dataSource;
var allData = grid.dataSource.data();
var code = this.value;
var notification = $("#notification").data("kendoNotification");
var timer = null;
clearTimeout(timer);
$.each(allData, function (index, item) {
if (item.ArticleID == code) {
clearTimeout(timer);
timer = null;
if (item.Quantity > item.PickedQuantity && item.PickedQuantity.toString().length < 4) {
var edit = function () {
if (item.PickedQuantity != item.Quantity && timer != null) {
grid.select("tr:eq(" + (index) + ")");
grid.editCell("tr:eq(" + (index + 1) + ") td:eq(" + (5) + ")");
} else {
//do nothing
}
}
item.PickedQuantity++;
item.dirty = true;
dataSource.sync();
if (item.PickedQuantity != item.Quantity) {
console.log("tik tok");
if (timer) {
clearTimeout(timer); //cancel the previous timer.
timer = null;
}
timer = setTimeout(edit, 3000);
} else {
clearTimeout(timer);
timer = null;
}
document.getElementById("txtBarcode").value = "";
} else {
if (item.PickedQuantity.toString().length > 4) {
notification.hide();
notification.show({
message: "Added item"
}, "upload-success");
} else {
notification.hide();
notification.show({
title: "Quantity Error",
message: "You already picked this item to the maximum"
}, "error");
document.getElementById("txtBarcode").value = "";
grid.select("tr:eq(" + (index) + ")");
grid.editCell("tr:eq(" + (index + 1) + ") td:eq(" + (5) + ")");
$('.focus :input').focus();
}
}
}
})
})

You can try delay function like this. The delay function should be outside of the each function.
var delay = (function() {
var timer = 0;
return function(callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
delay(function() {
//do something
}, 3000);

The problem was that var timer = null; had to be outside of the submit function to be properly cleared and set to null before assigning a new setTimeout() to it.

Related

Stopwatch frontend app stop, when tab is not active [duplicate]

So basically when I switch tabs, the countdown timer on a specific page just stops counting down and resumes when you return to the tab. Is there anyway to mitigate that so that it counts in the background or it accounts for the time you spend on another tab?
This is basically what I have for js:
document.getElementById('timer').innerHTML =
05 + ":" + 01;
startTimer();
function startTimer() {
var presentTime = document.getElementById('timer').innerHTML;
var timeArray = presentTime.split(/[:]+/);
var m = timeArray[0];
var s = checkSecond((timeArray[1] - 1));
if(s==59){m=m-1}
if(m<0){
return
} else if (m == 0 && s == 0) {
location.reload();
}
document.getElementById('timer').innerHTML =
m + ":" + s;
setTimeout(startTimer, 1000);
}
function checkSecond(sec) {
if (sec < 10 && sec >= 0) {sec = "0" + sec};
if (sec < 0) {sec = "59"};
return sec;
}
Any ideas whether the time could be done server side or something so that it can't be modified client side? If not, then whatever, but mainly just want to figure out how to make the countdown still work (or account for the time spent) when on another tab.
We can store the variable m and s values either globally or use the local storage to set the values after setting the inner HTML and get the stored values back whenever tabs were switched as:
Set values:
window.localStorage.setItem('minutes', m.toString()); //same for the seconds
Get values:
window.localStorage.getItem('minutes'); //same for the seconds
Hope this answers your questions.
Just a simple solution:
Add this piece of code.
<html>
<head>
<script>
(function() {
var $momentum;
function createWorker() {
var containerFunction = function() {
var idMap = {};
self.onmessage = function(e) {
if (e.data.type === 'setInterval') {
idMap[e.data.id] = setInterval(function() {
self.postMessage({
type: 'fire',
id: e.data.id
});
}, e.data.delay);
} else if (e.data.type === 'clearInterval') {
clearInterval(idMap[e.data.id]);
delete idMap[e.data.id];
} else if (e.data.type === 'setTimeout') {
idMap[e.data.id] = setTimeout(function() {
self.postMessage({
type: 'fire',
id: e.data.id
});
// remove reference to this timeout after is finished
delete idMap[e.data.id];
}, e.data.delay);
} else if (e.data.type === 'clearCallback') {
clearTimeout(idMap[e.data.id]);
delete idMap[e.data.id];
}
};
};
return new Worker(URL.createObjectURL(new Blob([
'(',
containerFunction.toString(),
')();'
], {
type: 'application/javascript'
})));
}
$momentum = {
worker: createWorker(),
idToCallback: {},
currentId: 0
};
function generateId() {
return $momentum.currentId++;
}
function patchedSetInterval(callback, delay) {
var intervalId = generateId();
$momentum.idToCallback[intervalId] = callback;
$momentum.worker.postMessage({
type: 'setInterval',
delay: delay,
id: intervalId
});
return intervalId;
}
function patchedClearInterval(intervalId) {
$momentum.worker.postMessage({
type: 'clearInterval',
id: intervalId
});
delete $momentum.idToCallback[intervalId];
}
function patchedSetTimeout(callback, delay) {
var intervalId = generateId();
$momentum.idToCallback[intervalId] = function() {
callback();
delete $momentum.idToCallback[intervalId];
};
$momentum.worker.postMessage({
type: 'setTimeout',
delay: delay,
id: intervalId
});
return intervalId;
}
function patchedClearTimeout(intervalId) {
$momentum.worker.postMessage({
type: 'clearInterval',
id: intervalId
});
delete $momentum.idToCallback[intervalId];
}
$momentum.worker.onmessage = function(e) {
if (e.data.type === 'fire') {
$momentum.idToCallback[e.data.id]();
}
};
window.$momentum = $momentum;
window.setInterval = patchedSetInterval;
window.clearInterval = patchedClearInterval;
window.setTimeout = patchedSetTimeout;
window.clearTimeout = patchedClearTimeout;
})();
</script>
</head>
</html>

Javascript pausing timer woes... Can't calculate it

I've built a series of timers that are designed to be started, paused and resumed on cue. Numbers update dynamically on my page when the timer ticks up. The issue I'm having is figuring out how to get the timer to start from where it left off before I paused it. I can get it to restart from scratch, but I unsure how to take the paused Date.now() value, and work it into the formula to display the correct value. It will be something stupid that I just cant figure out.
function ticking2(num) {
//IF TIMER IS PAUSED CANCEL THE ANIMATION FRAME
if (timerObjArray[num].paused == true) {
timerObjArray[num].restartTime = Date.now();
cancelAnimationFrame(id);
} else if (timerObjArray[num].paused == false) {
timerObjArray[num].initialTime = Date.now()
if (timerObjArray[num].restartTime != 0) {
//THIS IS THE LINE WHERE I AM HAVING TROUBLE
timerObjArray[num].milli = ((timerObjArray[num].initialTime - timerObjArray[num].initialDate) - (timerObjArray[num].initialTime - timerObjArray[num].restartTime)) / 1000;
} else {
timerObjArray[num].milli = ((timerObjArray[num].initialTime - timerObjArray[num].initialDate ) / 1000);
}
//THIS FUNCTION TAKES THE MS VALUE AND CONVERTS IT TO HH:MM:SS
convert(num, timerObjArray[num].milli * 1000);
id = requestAnimationFrame(function() {
ticking2(num);
});
}
}
Thanks for the help.
I don't have enough information so, I made a simple implementation. You can look at this to help determine what you're missing. You're welcome to use it.
Timer fiddle
Utility:
var timer = (function() {
let _timers = {};
let pub = {
start : function(id) {
if(!_timers[id]) _timers[id] = {on:true, intervals:[{start:new Date()}] };
else if(_timers[id].on) throw 'timer is already started: ' + id;
else {
_timers[id].on = true;
_timers[id].intervals.push({start:new Date()});
}
},
stop : function(id) {
if(!_timers[id]) throw 'timer does not exist, cannot be stopped: ' + id;
else if(!_timers[id].on) throw 'timer is already stopped: ' + id;
else {
_timers[id].on = false;
let interval = _timers[id].intervals[_timers[id].intervals.length -1];
interval.stop = new Date();
interval.total = interval.stop - interval.start;
}
},
read : function(id) {
if(!_timers[id]) throw 'timer does not exist, cannot be read: ' + id;
let total = 0;
for(let i=0; i<_timers[id].intervals.length; i++) {
if(_timers[id].intervals[i].total) total += _timers[id].intervals[i].total;
else total += (new Date()) - _timers[id].intervals[i].start;
}
return { intervals:_timers[id].intervals, total: total };
},
delete: function(id) {
if(!_timers[id]) throw 'timer does not exist, cannot be deleted: ' + id;
delete _timers[id];
}
};
return pub;
})();
Example usage:
$('.start').on('click', function(){
timer.start(123);
});
$('.stop').on('click', function(){
timer.stop(123);
});
$('.clear').on('click', function(){
timer.delete(123);
$('input').val('');
});
setInterval(function(){
let result = null;
try//obviously not a great pattern
{
result = timer.read(123);
} catch(ex){}
if(result) {
$('input').val(result.total);
}
}, 35);

How to break from Jquery click callback function

I am making my own version of simon Game, but the callback function inside click is not existing after user press wrong input. As a result function handler.patternRepeatPlayer() is getting called recursively for each element of array pattern.I want a solution to break from the else after callinghandler.patternRepeatPlayer just once. The program is working fine in strict mode, but only in non-strict mode inside else , I am not able to break from else.
-- can access the project on Git.
https://github.com/santosh1357/simonGame.git
The flow is like from html -> Function simonnGame.PatternGen from PatternGen -> handler.PatternRepeatPlayer -> PatternRepPlayer -> PatternMatcher -> userInput(here if wrong user input in non-strict mode) -> patternRepeatPlayer
This case is failing as in this case else is not existing after calling the function only once.
//Problematic Function.
userInput: function(){
var userPattern = new Array();var id;
$('img').click(function(){
id = parseInt(this.id,10); userPattern.push(id);handler.effect(id);
if(userPattern.indexOf(id) !== simonGame.PATTERN.indexOf(id)){
if($('.chkStrict:checked').val() === "on"){
var audio = new Audio('sounds/wrong.mp3');
audio.play();
setTimeout(function(){window.location.reload(true)},1000);
} else {
var audio = new Audio('sounds/wrong.mp3');
audio.play();
userPattern.length = 0;
handler.repeatFlag = true;
handler.patternRepeatPlayer(); ****//this is getting called recursivelly rather than quiting after calling once****
return ;
}
}
//End Problematic Functiom
I think there is some misunderstanding on how click callback functions work.
//Fullcode
var simonGame = {
COUNT: 0,
PATTERN: [],
SOUND:[{file:'sounds/sa.mp3'},{file:'sounds/re.mp3'},{file:'sounds/ga.mp3'},{file:'sounds/ma.mp3'},{file:'sounds/pa.mp3'},{file:'sounds/dha.mp3'},{file:'sounds/nee.mp3'}],
patternGen: function(){
var randomId;
randomId = Math.floor(Math.random() * 7);
simonGame.PATTERN.push(randomId);
if(simonGame.COUNT > 20){
alert("You have won the game!!");
window.location.reload(true);
}
simonGame.COUNT += 1;
//debugger;
//console.log("increase count true calling count display " + simonGame.COUNT);
handler.countDisplay();
//console.log("count gen true calling patternPlayer with PATTERN " + simonGame.PATTERN );
handler.patternRepeatPlayer();
}, //close patternGen
patternMatcher: function(genPattern){
//console.log("inside patternMatch");
var genPattern = simonGame.patternGen;
//setTimeout(function(){
//console.log("PATEERN: " + simonGame.PATTERN + "COUNT " + simonGame.COUNT );
//calling user input
console.log("calling user Input");
handler.userInput();
setTimeout(function(){
if(handler.repeatFlag === false){ //execute count gen only if repeat flag is false inside user INPUT
genPattern();
}
},simonGame.COUNT*2000);
//console.log("pattern check true, calling pattern gen");
//},simonGame.COUNT*5000); //c`enter code here`lose setTimeout
}, //close patternMatcher
} //close simonGame
var handler = {
countRepPlayer: 0,
repeatFlag: false,
patternRepeatPlayer: function(){
var repeater = setInterval(function(){
handler.effect(simonGame.PATTERN[handler.countRepPlayer]);
handler.countRepPlayer += 1;
if(handler.countRepPlayer > simonGame.COUNT){
clearInterval(repeater);
//setTimeout(function(){
simonGame.patternMatcher();
//},1000);
handler.countRepPlayer = 0;
}
},1000);//close sestInterval
}, //close patternRepeatPlayer
effect: function(id){
var img = document.getElementById(id);
if(img !== null && id !== undefined){
$( img ).fadeIn(100).fadeOut(100).fadeIn(100);//fadeOut(200).fadeIn(200);
//debugger;
var audio = new Audio(simonGame.SOUND[id].file);
audio.play();
//console.log("id inside effect " + id)
}
},//close effect
countDisplay: function(){
document.getElementById("count").innerHTML = "Count: " + simonGame.COUNT;
}, //close countIncrease
userInput: function(){
var userPattern = new Array();var id;
$('img').click(function(){
id = parseInt(this.id,10);
userPattern.push(id);
handler.effect(id);
console.log(" user " + userPattern);
console.log(" pattern " + simonGame.PATTERN);
if(userPattern.indexOf(id) !== simonGame.PATTERN.indexOf(id)){
console.log(" WRONG USER INPUT ");
if($('.chkStrict:checked').val() === "on"){
var audio = new Audio('sounds/wrong.mp3');
audio.play();
setTimeout(function(){window.location.reload(true)},1000);
} else {
console.log("inside else " );
var audio = new Audio('sounds/wrong.mp3');
audio.play();
userPattern.length = 0;
handler.repeatFlag = true;
handler.patternRepeatPlayer(); ****//this is getting called recursivelly rather than quiting after calling once**.**
return ;
}
}
//reset the userPattern Array
if(userPattern.length === simonGame.PATTERN.length){
userPattern.length = 0;
}
}); //close click.
}
} //close handler
Yes, It will be called recursively, because you set interval for it.
Here you can modify your code:
patternRepeatPlayer: function(){
var repeater = setInterval(function(){
handler.effect(simonGame.PATTERN[handler.countRepPlayer]);
handler.countRepPlayer += 1;
if(handler.countRepPlayer > simonGame.COUNT){
clearInterval(repeater);
//setTimeout(function(){
simonGame.patternMatcher();
//},1000);
handler.countRepPlayer = 0;
}
},1000);//close sestInterval
}
To: (EDITED)
function myCallbackFunction(repeater){
handler.effect(simonGame.PATTERN[handler.countRepPlayer]);
handler.countRepPlayer += 1;
if(handler.countRepPlayer > simonGame.COUNT){
clearInterval(repeater);
simonGame.patternMatcher();
handler.countRepPlayer = 0;
}
}
patternRepeatPlayer: function(){
var repeater = setInterval(function() {myCallbackFunction(repeater);}, 1000);
}
And where you need to call it once, just call myCallbackFunction(repeater)

pause and continue using setInterval

what i want to achieve is when you click this href pause/continue the timer pauses and continues when pressed again.
<script>
$(document).ready(function () {
var counter = 10;
var id = setInterval(function() {
counter--;
if(counter > 0) {
var msg = 'You can continue ' + counter + ' seconds';
$('#notice').text(msg);
} else {
$('#notice').hide();
$('#download').show();
clearInterval(id);
}
}, 1000);
});
</script>
My HTML in relevance to the jQuery is here if you need.
Continue !!<p id="notice">
You can continue in 10 seconds</p>
Well, I would just have your pause event set a boolean, and then check that boolean before you decrement your counter:
pause/continue
and
var ispaused=false;
function setPause(){
ispaused=!ispaused;
}
and
$(document).ready(function () {
var counter = 10;
var id = setInterval(function() {
if(!ispaused){ ////the only new line I added from your example above
counter--;
if(counter > 0) {
var msg = 'You can continue ' + counter + ' seconds';
$('#notice').text(msg);
} else {
$('#notice').hide();
$('#download').show();
clearInterval(id);
}
}
}, 1000);
});
That should do it, right?

How can I re enable a setInterval after I called clearInterval?

Here's my javascript:
<script type="text/javascript">
var timer;
$(document).ready(function () {
timer = setInterval(updatetimerdisplay, 1000);
$('.countdown').change(function () {
timer = setInterval(updatetimerdisplay, 1000);
});
function updatetimerdisplay() {
$(".auctiondiv .auctiondivleftcontainer .countdown").each(function () {
var newValue = parseInt($(this).text(), 10) - 1;
$(this).text(newValue);
if (newValue >= 9) {
$(this).css("color", "");
$(this).css("color", "#4682b4");
}
if (newValue == 8) {
$(this).css("color", "");
$(this).css("color", "#f3982e");
}
if (newValue == 5) {
$(this).css("color", "");
$(this).css("color", "Red");
}
if (newValue <= 1) {
//$(this).parent().fadeOut();
clearInterval(timer);
}
});
}
});
var updateauctionstimer = setInterval(function () {
$("#refreshauctionlink").click();
}, 2000);
function updateauctions(response) {
var data = $.parseJSON(response);
$(data).each(function () {
var divId = "#" + this.i;
if ($(divId + " .auctiondivrightcontainer .latestbidder").text() != this.b) {
$(divId + " .auctiondivrightcontainer .latestbidder").fadeOut().fadeIn();
$(divId + " .auctiondivrightcontainer .auctionprice .actualauctionprice").fadeOut().fadeIn();
$(divId + " .auctiondivleftcontainer .countdown").fadeOut().fadeIn();
}
$(divId + " .auctiondivrightcontainer .latestbidder").html(this.b);
$(divId + " .auctiondivrightcontainer .auctionprice .actualauctionprice").html(this.p);
if ($(divId + " .auctiondivleftcontainer .countdown").text() < this.t) {
$(divId + " .auctiondivleftcontainer .countdown").html(this.t);
}
});
}
</script>
Basically, I want to turn the timer back on, if any .countdown element has it's text change.
The text will change because of an AJAX call I use to update that value.
Currently the timer doesn't re enable and the countdown freezes after the value of .Countdown is changed. I think that the change() event fires when the text of an element changes. Is this correct?
Any glaring mistakes on my part?
Your code contains a loop and condition:
function updatetimerdisplay() {
$(".auctiondiv .auctiondivleftcontainer .countdown").each(function () {
...
if (newValue <= 1) {
//$(this).parent().fadeOut();
clearInterval(timer);
}
...
}
}
It's very likely that one of these elements have a value which satisfy the condition newValue <= 1. In that case, the timer will stop. Even when you change the contents of an input field, the timer will immediately stop after that run.
If you have to support multiple timers, use a wallet of timers (var timers = {};timers.time1=... or var timers = [];timers[0] = ...). If you have to support only a few timeouts, you can even use variables (var timer1=... ,timer2=..., timer3=...;).
You are trying to bind the change event before the elements exist. Put that code inside the ready event handler:
$(document).ready(function () {
timer = setInterval(updatetimerdisplay, 1000);
$('.countdown').change(function () {
timer = setInterval(updatetimerdisplay, 1000);
});
});
You also might want set the timer variable to an identifiable value when the timer is off (e.g. null), so that you can check for that value before you start a timer to prevent from starting multiple timers.

Categories

Resources