javascript timer/duration giving wrong calculation after while - javascript

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>

Related

Countdown timer is lagging behind

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/

Display time format in a running stopwatch

I'm making a stopwatch and out of esthetic reasons I want the output to display: 00:00:00:000. The problem is that when my stopwatch is running I'm having a hard time getting it to except a 0 in front when the value < 10.
window.addEventListener('load', function() {
var display = document.getElementById('display-area');
var toggle = document.getElementById('toggle-button');
var reset = document.getElementById('reset-button');
var ms,
difference,
interval,
hours,
minutes,
seconds,
timer = 0;
function start() {
difference = Date.now();
interval = window.setInterval(update, 10);
timer = 1;
};
function stopp() {
window.clearInterval(interval);
timer = 0;
};
function nullstill() {
ms = 0;
seconds = 0;
minutes = 0;
hours = 0;
display.value = '00:00:00:000';
};
function update() {
ms += elapsedTime();
if (ms >= 1000) {
seconds += 1;
ms = 0;
}
if (seconds >= 60) {
minutes += 1;
seconds = 0;
}
if (minutes >= 60) {
hours += 1;
minutes = 0;
}
display.value = hours + ':' + minutes +':' + seconds +':'+ ms;
};
function elapsedTime() {
var now = Date.now();
elapsed = now - difference;
difference = now;
return elapsed;
};
nullstill();
toggle.addEventListener('click', function() {
console.log(timer);
if (timer != 1) {
start();
} else {
stopp();
}
});
reset.addEventListener('click', function() {
nullstill();
});
});
How do I make it work?
Regards,
An integer will never hold a 0 in front of the number. This is a fairly easy fix. You will just need to use some string concatenation.
display.value = (hours < 10 ? "0"+hours : hours) + ':' + (minutes < 10 ? "0"+minutes : minutes) +':' + (seconds < 10 ? "0"+seconds: seconds) +':'+ ms;
The syntax I have used is called a ternary operator. Here is a little bit about how it works. Basically, it is a simplified if statement which can be used inline.
( condition ? {if true, run this } : {else, run this})
Here's a useful little example that shows you a convenient way to add leading zeros to numbers in Javascript. If you have a number like 53, and want 6 number places (eg 4 leading zeros in the case of 56), you just add (1e6+53+'').slice(-6) and that will give you 000053 because 1e6 means 1 with 6 zeros after it, and slice with a negative number starts from the end and chops out 6 places in this case, so 100000053 becomes 000053
hours=0,minutes=1,seconds=20,ms=7;
document.getElementById('t').innerHTML=
(1e2+hours+'').slice(-2) + ':' +
(1e2+minutes+'').slice(-2) +':' +
(1e2+seconds+'').slice(-2) +':'+
(1e3+ms+'').slice(-3);
<div id='t'></div>
Notice the 1 or 2 leading zeros in the case of ms is handled. And you can adjust the number of leading zeros easily.
This requires string concatenation when the value is less than 10. I would create three separate variables for the hours, minutes and seconds. That way the code is more clean and readable.
var displayHours = (hours >= 10) ? hours : "0" + hours;
var displayMins = (minutes >= 10) ? minutes : "0" + minutes;
var displaySeconds = (seconds >= 10) ? seconds : "0" + seconds;
display.value = displayHours + displayMins + displaySeconds + ":" + ms;

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);

How to stop the timer once you end an exam?

I want to stop this timer when I end the exam, but before it reaches zero. Kindly help me out on scripts please. Thanks.
JavaScript code:
var cnt = 165*60; // 165 minutes (2 hours & 45 minutes) convert to seconds
function countdown() {
if (cnt < 0) {
document.f.c.value = "- : - - : - -" ;
}
else {
hour = Math.floor(cnt / 3600);
totalmin = Math.floor(cnt / 60);
min = totalmin - (hour * 60);
sec = cnt - (totalmin * 60);
if (sec < 10) { sec = "0" + sec;}
if (min < 10) {min = "0" + min;}
if (hour < 10) {hour = "0" + hour;}
document.f.c.value = hour + ":" + min + ":" + sec;
cnt--;
_timer = setTimeout("countdown()", 1000);
}
}
var _timer = setTimeout("countdown()", 1000); // tick
I assume you meant that you want to end the timer before the countdown reaches 0.
First of all, you should use setInterval instead. It should work in all major browsers (including IE). It is just a slightly nicer way to express "I want this to happen every so often." According to the MDN, it:
Calls a function repeatedly, with a fixed time delay between each call to that function.
Here's how you would use it:
var cnt = 165*60; // 165 minutes (2 hours & 45 minutes) convert to seconds
function countdown() {
if (cnt < 0) {
document.f.c.value = "- : - - : - -" ;
}
else {
hour = Math.floor(cnt / 3600);
totalmin = Math.floor(cnt / 60);
min = totalmin - (hour * 60);
sec = cnt - (totalmin * 60);
if (sec < 10) {sec = "0" + sec;}
if (min < 10) {min = "0" + min;}
if (hour < 10) {hour = "0" + hour;}
document.f.c.value = hour + ":" + min + ":" + sec;
cnt--;
if(cnt <= 0) { # Stops the timer when it reaches 0.
clearInterval(_interval);
}
}
}
var _interval = setInterval(countdown, 1000);
And somewhere in your page have a button that will stop the timer.
<input type="button" value="Done" onclick="clearInterval(_interval)">
Although to be honest, having a countdown timer freaks me out. I'd rather have a count-up timer. :-)
In your else part
check
if(current_time < total_time)
{
//Set TimeOut
}
I think your best bet here is to use setInterval rather than setTimeout.
setInterval returns a handle to the interval. clearInterval(handle) will cancel that interval. Here's some pseudo to get you started:
var global_timer;
function countdown(){
// do some countdown stuff
if([we're done]) {
window.clearInterval(global_timer);
}
}
global_timer = window.setInterval("countdown()", 1000);
In the code that you execute when user ends the exam, probably after button click, just add such line:
window.clearTimeout(_timer);
And the timer will stop ticking.

Categories