Countdown timer is lagging behind - javascript

I am trying to create countdown timer between 2 dates but the time is lagging behind after a while.
My PHP backend returns the difference between current time and X time in the future, for example current time and 2 hours in advance. This difference is passed to my HTML frontent in a .countdown class in the following format 03:20:15 which I use a javascript function to countdown the difference. Here is my function:
$(".countdown").each(function() {
var $e = $(this);
var interval = setInterval(function() {
var timer2 = $e.html();
var timer = timer2.split(':');
var hours = parseInt(timer[0], 10);
var minutes = parseInt(timer[1], 10);
var seconds = parseInt(timer[2], 10);
--seconds;
minutes = (seconds < 0) ? --minutes : minutes;
hours = (minutes < 0) ? --hours : hours;
if(hours < 0) {
clearInterval(interval);
window.location.reload();
} else {
seconds = (seconds < 0) ? 59 : seconds;
seconds = (seconds < 10) ? '0' + seconds : seconds;
minutes = (minutes < 0) ? 59 : minutes;
minutes = (minutes < 10) ? '0' + minutes : minutes;
hours = (hours < 10) ? '0' + hours : hours;
$e.html(hours + ':' + minutes + ':' + seconds);
}
}, 1000);
});
The code works as expected but after a few minutes, lets say 2-3 minutes, if you refresh the page or open it in a new window you will see that the countdown timer was lagging behind by seconds/minutes. Does someone know what Im doing wrong?

You should compute the difference between (new Date()) and the target date. Use that difference and format new HTML string instead of parsing it to a hour, minutes, seconds value for decrementing.
details
The setInterval api specs suggest that delays due to CPU load, other tasks, etc, are to be expected.
https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
Your handler is called at approximately equal intervals while you consider them to be exact. At first iteration the actual time may differ from a planed time by some small amount (let say 4ms). Yet you are changing your counter by 1000 ms. As many more iterations passed this difference accumulates and become noticeable. A few minutes is enough to make this happens.
If you, on the other hand, pre-compute the target date-time value and will use the difference between current time and the target time your code will not be sensible to api inexactness.
$(".countdown").each(function () {
var $e = $(this);
const totalSeconds = (dt) => Math.floor(dt.valueOf() / 1000);
const f1 = (timer2) => {
var timer = timer2.split(':');
var tdt = new Date().setHours(
parseInt(timer[0]) + tdt.getHours(),
parseInt(timer[1]) + tdt.getMinutes(),
parseInt(timer[2]) + tdt.getSeconds());
return totalSeconds(tdt);
};
const targetTime = f1($e.html());
setInterval(function () {
var timeSpan = targetTime - totalSeconds(new Date());
if (timeSpan < 0) {
window.location.reload();
} else {
var seconds = timeSpan % 60;
var totalMinutes = Math.floor(timeSpan / 60);
var minutes = totalMinutes % 60;
var hours = Math.floor(totalMinutes / 60);
$e.html(hours + ':' + minutes + ':' + seconds);;
}
}, 1000);
});
see also:
https://jsfiddle.net/8dygbo9a/1/

Related

How do I subtract time from a timer in Javascript if a certain condition is true?

I am making a riddle, where the people who try to solve it have 45 minutes to solve the riddle, and when they don't answer correctly, I want the timer to go down five minutes, to prevent them from just guessing the answers. How could I do it, I am very new to using javascript, this is the first time I'm working with it.
function startTimer(duration, display) {
var start = Date.now(),
diff,
minutes,
seconds;
var cat1 = ($("input[#name=Verdachte]:checked").val() != "2");
var cat2 = ($("input[#name=Moordwapen]:checked").val() != "4");
function timer() {
diff = duration - (((Date.now() - start) / 1000) | 0);
minutes = (diff / 60) | 0;
seconds = (diff % 60) | 0;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.textContent = minutes + ":" + seconds;
if (diff <= 0) {
start = Date.now() + 1000;
}
};
timer();
setInterval(timer, 1000);
}
window.onload = function () {
var fortyfiveMinutes = 60 * 45,
display = document.querySelector('#time');
startTimer(fortyfiveMinutes,display);}
I want the timer to go down five minutes when cat1 is true, and/or when cat2 is true.
Inside of timer, just check the input, and if it is true, disable the input and increase the time:
var cat1 = $("input[#name=Verdachte]:checked");
if(cat1.val() === "2") {
cat1.val("you are right :)");
cat1.attr("disabled", true);
start -= 1000 * 60 * 5;
}
//...
... that would be even more elegant with event handlers ...

javascript timer/duration giving wrong calculation after while

couple of days ago, I found a javascript code which I thought that I can use it to calculate the login time for users.. but after tested it in my application, I found that there's a difference in time after while. So, I decided to use an chrome extension called "Staying Alive for Google Chrome", it worked perfectly "or that I was hoping to.. and to be honest, it kinda good", but after that I found the same problem as picture attached.
and here's code for javscript I found:
/*===============*/
var seconds = 0;
var minutes = 0;
var hours = 0;
var t;
function add() {
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
}
}
$('#totalTime').html ( "" + (hours ? (hours > 9 ? hours : "0" + hours) : "00") + ":" + (minutes ? (minutes > 9 ? minutes : "0" + minutes) : "00") + ":" + (seconds > 9 ? seconds : "0" + seconds) );
timer();
}
function timer() {
t = setTimeout(add, 1000);
}
/*===============*/
So, I hope that someone can help me, how to make timer run decently, or has a better solution, because I have more than 6 timers in this page..
P.S. this application coded using php and javascript and I've an ajax call every 5000 milliseconds running in same page.
P.P.S. one of my friends suggested webworkers , but I really don't know a lot about it.
thanks a lot
This behaviour is expected due to the fact JavaScript is executed in a single thread in the browser. Basically, the delay amount of the setTimeout and setInterval functions should be considered a minimum.
For example, if you would execute a function resulting in heavy DOM manipulation, this could postpone the execution of the callback.
For more information on this topic, read John Resig's post on the the execution order of JavaScript timers.
Your friend's suggestion could provide a solution as WebWorkers are executed in a different thread and hence are less subjective to blocking DOM manipulation. However, I would suggest you'd handle the time calculation on the server side. For example by creating an entry for the stream in a database containing at least the start date and time of the stream. Then either:
Let your JavaScript application poll the server to sync the time
Alternatively and preferably, set up a streaming connection using a WebSocket.
The best way to measure time is to save the value of the system clock (provided by Date-Object) at start and subtract it from the current timestamp.
An example:
const totalTime = document.querySelector('#totalTime');
const startTime = Date.now();
function updateClock() {
// JS Date counts in ms, divide by 1000 to get seconds
// |0 does the same as Math.floor(value)
const elapsedTime = (Date.now() - startTime) / 1000 |0;
const seconds = elapsedTime % 60;
const minutes = Math.floor(elapsedTime / 60) % 60;
const hours = Math.floor(elapsedTime / 3600) % 24;
totalTime.textContent =
(hours < 10 ? '0' : '') + hours + ':' +
(minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds;
}
window.setInterval(updateClock, 1000);
<div id="totalTime"></div>
EDIT 1 "from the one who asked":
I did some edits to your code cos I needed to add some options (reset, pause and resume).
I thought it could help any one, so I post it...
class Clock {
constructor(element, offset = 0) {
if(!(element && element instanceof HTMLElement))
throw new Error('Parameter element must be a valid HTML Element');
this._startTime = 0;
this._offset = offset;
this._interval = null;
this._element = element;
}
tick() {
const elapsedTime = (Date.now() - this._startTime) / 1000 |0;
const seconds = elapsedTime % 60;
const minutes = elapsedTime / 60 % 60 |0;
const hours = elapsedTime / 3600 % 24 |0;
this._element.textContent =
(hours < 10 ? '0' : '') + hours + ':' +
(minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds;
}
pause () {
if(this._interval) {
this._offset = Date.now() - this._startTime;
window.clearInterval(this._interval);
this._interval = null;
}
}
resume () {
if(!this._interval) {
this._startTime = Date.now() - this._offset;
this._interval = setInterval(this.tick.bind(this), 1000);
this.tick();
}
}
reset () {
this.pause();
this._offset = 0;
this._element.textContent = '00:00:00';
}
};
let clock = new Clock(document.querySelector('#totalTime'), 30000);
document.querySelector('#pause').addEventListener('click', () => { clock.pause() });
document.querySelector('#resume').addEventListener('click', () => { clock.resume() });
document.querySelector('#reset').addEventListener('click', () => { clock.reset() });
clock.resume();
<div id="totalTime"></div>
<button id="pause">Pause</button>
<button id="resume">Resume</button>
<button id="reset">Reset</button>

timezone javascript in my timer

I have found a code but i dont know to add timezone . i want to detect the timer from the timezone of the other country like denmark/copenhagen. thank you. this is my code.
<script type="text/javascript">
ElapsedTimeLogger = function(dateElementId, elapsedElementId, hiden, interval) {
var container = $(elapsedElementId);
var time = parseDate($(dateElementId).val());
var interval = interval;
var timer;
function parseDate(dateString) {
var date = new Date(dateString);
return date.getTime();
}
function update() {
var systemTime = new Date().getTime();
elapsedTime = systemTime - time;
container.html(prettyPrintTime(Math.floor(elapsedTime / 1000)));
$(hiden).val(prettyPrintTime(Math.floor(elapsedTime / 1000)));
}
function prettyPrintTime(numSeconds) {
var hours = Math.floor(numSeconds / 3600);
var minutes = Math.floor((numSeconds - (hours * 3600)) / 60);
var seconds = numSeconds - (hours * 3600) - (minutes * 60);
if (hours < 10) hours = "0" + hours;
if (minutes < 10) minutes = "0" + minutes;
if (seconds < 10) seconds = "0" + seconds;
var time = hours + ":" + minutes + ":" + seconds;
return time;
}
this.start = function() {
timer = setInterval(function() {update()}, interval * 1000);
}
this.stop = function() {
clearTimeout(timer);
}
}
$(document).ready(function () {
var timeLogger = new ElapsedTimeLogger("#date", "#elapsed","#stoppedid", 1);
timeLogger.start();
$("#confirm").click(function() { //Stop timer upon clicking the Confirm Button
timeLogger.stop();
});
});
</script>
thank you. i dont know javascript. i know php only. i tried to put
before the code is running. i already save a time from europe/copenhagen. but when the timer is running. it says 6:00:01 abd counting.. but i want to run like this 0:00:01 and counting. and my idea the time from europe and time in my country is 6 hours. i want to run the time from europe not my country. because i save the time from europe using php. see bellow the code for save the time.
date_default_timezone_set("Europe/Copenhagen");
but wont work. i didnt found the solution
Analyzing this code, I rewrote the needed HTML to see what the code do. It's simply creates a counter in format hh:mm:ss and shows on screen, this counter show the time passed since the date informed.
to add the user timezone to reflect in your timer, you just need to recalculate the seconds inside the prettyPrintTime(numSeconds) function before use it to get hours, minutes and seconds.
function prettyPrintTime(numSeconds) {
var tzOffset = new Date().getTimezoneOffset(); // get the timezone in minutes
tzOffset = tzOffset * 60; // convert minutes to seconds
numSeconds -= tzOffset; // recalculate the time using timezone
var hours = Math.floor(numSeconds / 3600);
var minutes = Math.floor((numSeconds - (hours * 3600)) / 60);
var seconds = numSeconds - (hours * 3600) - (minutes * 60);
if (hours < 10) hours = "0" + hours;
if (minutes < 10) minutes = "0" + minutes;
if (seconds < 10) seconds = "0" + seconds;
var time = hours + ":" + minutes + ":" + seconds;
return time;
}
Take a look at the working code:
https://jsfiddle.net/4c6xdcpr/
function getClientTimeZone() {
var offset = new Date().getTimezoneOffset(),
o = Math.abs(offset);
return (offset < 0 ? "+" : "-") + ("00" + Math.floor(o / 60)).slice(-2) + ":" + ("00" + (o % 60)).slice(-2);
}
// Display Output
alert(getClientTimeZone());

JS countdown timer with cookie issues

We have a 24 hour countdown timer. Problem is, whenever the page is refreshed the timer restarts. How can we create a cookie so it doesn't restart for the same user/when refreshed? And if if goes down to 0 restarts again?
What we have so far:
<script type = "text/javascript">
var totalSeconds;
function initiate(seconds)
{
totalSeconds = parseInt(seconds);
setInterval("timeUpdate()", 1000);
}
function timeUpdate()
{
var seconds = totalSeconds;
if(seconds > 0)
{
totalSeconds--;
var hours= Math.floor(seconds/3600);
seconds %= 3600;
var minutes = Math.floor(seconds/60);
seconds %= 60;
var timeIs = ((hours < 10) ? "0" : "") + hours + ":" + ((minutes < 10) ? "0" : "") + minutes + ":" + ((seconds < 10) ? "0" : "") + seconds;
document.getElementById("timeLeft").innerHTML = "" + timeIs;
}
else
{
document.getElementById("timeLeft").innerHTML = '';
document.getElementById("message").innerHTML = '';
}
}
initiate(24 * 60 * 60);
</script>
document.cookie="name=" + cookievalue;
alert("Setting Cookies : " + "name=" + cookievalue );
SEE HERE
First we need to have functions to set and read cookies. For this use the functions given in this answer How do I create and read a value from cookie?
So, we have two functions createCookie and setCookie in our code.
Now set and get cookie on page load as given below
var
//Get time started
timeStarted = getCookie('timeStarted'),
//To store total seconds left
totalSeconds,
//Current Time
currentTime = parseInt(new Date()/1000),
//Timer Length
timerLength = 24 * 60 * 60;
if(timeStarted == "") {
//Time not yet started. Start now.
createCookie('timeStarted', currentTime, 365);
//We started time just now. So, we have full timer length remaining
totalSeconds = timerLength;
} else {
//Calculate total seconds remaining
totalSeconds = timerLength - (currentTime - timeStarted)%(timerLength);
}
Now initialize as given below
initialize(totalSeconds);
Both your functions work fine and keep them.
I used 365 days as the cookie period. We just need the start time.
Change the code as per your requirements.
Let me know if you need any clarification about this code.

javascript/jquery countdown timer with JSfiddle example?

I am building a few things and one of them is a countdown timer, the countdown will never be over an hour so all I need to do is countdown minutes and seconds.
I have it partially working, but the problem is with the leading zeros. I got it to work in the seconds but not with the minutes.
Check out my example http://jsfiddle.net/cgweb87/GHNtk/
JavaScript
setInterval(function() {
var timer = $('span').html();
timer = timer.split(':');
var minutes = timer[0];
var seconds = timer[1];
seconds -= 1;
if (minutes < 0) return;
if (minutes < 10 && length.minutes != 2) minutes = '0' + minutes;
if (seconds < 0 && minutes != 0) {
minutes -= 1;
seconds = 59;
}
else if (seconds < 10 && length.seconds != 2) seconds = '0' + seconds;
$('span').html(minutes + ':' + seconds);
}, 1000);
HTML
<span>10:10</span>
What I want to happen is the countdown timer can begin anywhere under 1 hour, it will count down with leading zeros ie in this format;
08:49
46:09
And when it reaches the countdown to simply just display:
00:00
Thanks for any input, and I don't want to use plugins, I want to learn it.
setInterval returns an identity you can use later to clearInterval:
var interval = setInterval(function() {
/* snip */
$('span').html(minutes + ':' + seconds);
if (parseInt(minutes, 10) == 0 && parseInt(seconds, 10) == 0)
clearInterval(interval);
}, 1000);
And, to avoid the ever-increasing minutes -- 00000001:42 -- either:
change length.minutes to minutes.length in your prefix test.
cast the values to Numbers when retrieving -- var minutes = parseInt(timer[0], 10); -- and just test if (minutes < 10) ....
Taking option #2, here's an update: http://jsfiddle.net/BH8q9/
to check the length of a string, it is not
length.minutes
length.seconds
it is
minutes.length
seconds.length
Made a few simple changes to your code and it works as you'd like:
setInterval(function() {
var timer = $('span').html();
timer = timer.split(':');
var minutes = timer[0];
var seconds = timer[1];
seconds -= 1;
if (minutes < 0) return;
if (seconds < 0 && minutes != 0) {
minutes -= 1;
seconds = 59;
}
else if (seconds < 10 && length.seconds != 2) seconds = '0' + seconds;
if ((minutes < 10) && ((minutes+'').length < 2)) minutes = '0' + minutes;
$('span').html(minutes + ':' + seconds);
}, 1000);
I moved the if ((minutes < 10).... line down to happen after the minutes -= 1; otherwise at 9:59, you won't get the extra 0. Also length.minutes is the wrong way around, it'd need to be minutes.length -- but to make sure it's being treated as a string (which has a length, whereas a number doesn't), I added a blank string to it and then took the length of that.. This is what ((minutes+'').length < 2 does (checks that you have the leading zero).. This is really the best way to accomplish it, but it's the closest to your existing code.
I understand that an answer has already being accepted but would like to throw in my 2c: I like to avoid extra coding whenever possible. Using Jonathan Lonowski's approach, I would improve it like:
var interval = setInterval(function() {
var timer = $('span').html().split(':');
//by parsing integer, I avoid all extra string processing
var minutes = parseInt(timer[0],10);
var seconds = parseInt(timer[1],10);
--seconds;
minutes = (seconds < 0) ? --minutes : minutes;
if (minutes < 0) clearInterval(interval);
seconds = (seconds < 0) ? 59 : seconds;
seconds = (seconds < 10) ? '0' + seconds : seconds;
minutes = (minutes < 10) ? '0' + minutes : minutes;
$('span').html(minutes + ':' + seconds);
}, 1000);

Categories