I'm a bit stuck with a javascript countdown script. It is designed to grab the amount of time from a div and then count this down and submit a form.
Here is the code:
function secondPassed() {
var str=$("#countdown").text();
var pieces = str.split(":");
var seconds = (Number(pieces[0]) * 60) + Number(pieces[1]);
var minutes = Math.round((seconds - 30) / 60);
remainingSeconds = seconds % 60;
if (remainingSeconds < 10) {
remainingSeconds = "0" + remainingSeconds;
}
document.getElementById('countdown').innerHTML = minutes + ":" + remainingSeconds;
if (seconds == 0) {
clearInterval(countdownTimer);
document.qForm.submit();
} else {
seconds--;
}
}
var countdownTimer = setInterval(secondPassed, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>Question 1 of 20<time id="countdown">5:00</time></h3>
<form name="qForm">
<input type="radio"> 219
<input type="submit" value="Submit">
</form>
The issue is found in the first few lines which I have had to modify. How do I get it to grab the text from my time tag, but instead I'm stuck with it being like this:
var pieces = str.split(":");
How do I get it to pull in the time value from my time tag instead?
Actually, your logic can be greatly simplified. What you need to realize is that since you are reading from the .innerHTML of the element, you will have to update it so that you actually successfully decrement the time: otherwise you are perpetually stuck at the starting time, since you are reading from the text node and yet it is never updated.
Step 1: Reading the .innerHTML of your element
In order to get the innerHTML of the <time> element, you can do it as simple as:
var pieces = document.getElementById('countdown').innerHTML.split(":");
However, we can always cache this DOM node, since we need to update its innerHTML later anyway:
var countdown = document.getElementById('countdown');
var pieces = countdown.innerHTML.split(":");
Step 2: Counting down
You've got the time parsing logic correct, so all you need is to keep track of the total seconds left on your clock, and remember to decrement it by 1 second:
var totalSeconds = (Number(pieces[0]) * 60) + Number(pieces[1]);
// Countdown
totalSeconds--;
Step 3. Update your <time> element so that countdown time is updated
Here we simply get decremented totalSeconds, and calculate the remaining minutes and seconds left. Minutes left is simply the total seconds divided by 60 and floored, while seconds left is simply the modulus of total seconds. You've got the calculations right to begin with, I simply made it easier to read.
Then, you want to update your countdown element's innerHTML (remember that we cahced that DOM node in step 1?). We use tenary operators to simplify the logic of adding 0 to the front of single-digit seconds:
// Update <time>
var currentMinutes = Math.floor(totalSeconds / 60);
var currentSeconds = totalSeconds % 60;
countdown.innerHTML = currentMinutes + ':' + (currentSeconds < 10 ? '0' + currentSeconds : currentSeconds);
Step 4: Check if timer has run out
This is the simple part: at the end of everything, just check if totalSeconds is now 0. If it is, clear the interval and submit the form:
// Submit form when we hit 0
if (totalSeconds === 0) {
window.clearInterval(countdownTimer);
document.getElementById('qForm').submit();
}
Here is a proof-of-concept example, but I have substituted the time so that we have 5 seconds to countdown (to simplyify testing) and that the form submission is commented out and replaced by an alert():
function secondPassed() {
var countdown = document.getElementById('countdown');
var pieces = countdown.innerHTML.split(":");
var totalSeconds = (Number(pieces[0]) * 60) + Number(pieces[1]);
// Countdown
totalSeconds--;
// Update <time>
var currentMinutes = Math.floor(totalSeconds / 60);
var currentSeconds = totalSeconds % 60;
countdown.innerHTML = currentMinutes + ':' + (currentSeconds < 10 ? '0' + currentSeconds : currentSeconds);
// Submit form when we hit 0
if (totalSeconds === 0) {
window.clearInterval(countdownTimer);
alert('Will submit form!');
// document.getElementById('qForm').submit();
}
}
var countdownTimer = setInterval(secondPassed, 1000);
<h3>Question 1 of 20<br /><time id="countdown">0:05</time></h3>
<form name="qForm">
<input type="radio"> 219
<input type="submit" value="Submit">
</form>
Alternative recommendation: store data in HTML5 data- attribute
It's often not ideal to read data directly from innerHTML of a DOM node, because sometimes you might want to update the HTML without affected the stored data. In that case, you can always use the HTML5 dataset API, where arbitrary data is stored in data- attributes.
In the code snippet below, we can store the countdown in the date-countdown attribute:
<time id="countdown" data-countdown="0:05"></time>
And we can simply create a helper function to synchronize the data-countdown value and write it to the innerHTML:
function updateTimer() {
var countdown = document.getElementById('countdown');
countdown.innerHTML = countdown.dataset.countdown;
}
You can call this function:
On pageload, and
Every time the countdown is updated
See the updated proof-of-concept below:
function secondPassed() {
var countdown = document.getElementById('countdown');
var pieces = countdown.dataset.countdown.split(":");
var totalSeconds = (Number(pieces[0]) * 60) + Number(pieces[1]);
// Countdown
totalSeconds--;
// Update <time>
var currentMinutes = Math.floor(totalSeconds / 60);
var currentSeconds = totalSeconds % 60;
countdown.dataset.countdown = currentMinutes + ':' + (currentSeconds < 10 ? '0' + currentSeconds : currentSeconds);
// Update innerHTML
updateTimer();
// Submit form when we hit 0
if (totalSeconds === 0) {
window.clearInterval(countdownTimer);
alert('Will submit form!');
// document.getElementById('qForm').submit();
}
}
function updateTimer() {
var countdown = document.getElementById('countdown');
countdown.innerHTML = countdown.dataset.countdown;
}
var countdownTimer = setInterval(secondPassed, 1000);
updateTimer();
<h3>Question 1 of 20<br /><time id="countdown" data-countdown="0:05"></time></h3>
<form name="qForm">
<input type="radio"> 219
<input type="submit" value="Submit">
</form>
Not entirely sure why you would want to be select this from the DOM, would it not just be better to know you are calculating 5 minutes and save yourself the hassle of parsing the value from the DOM.
Alternatively, set the 'datetime' property of the tag.
https://www.w3schools.com/tags/tag_time.asp
See below for an example of reading this:
https://www.w3schools.com/jsref/prop_time_datetime.asp
You can use below code
var countdownTimer;
function secondPassed() {
var str = document.getElementById('countdown').innerHTML;
var pieces = str.split(":");
var seconds = (Number(pieces[0]) * 60) + Number(pieces[1]);
console.log(seconds);
var minutes = 0;
if (seconds == 0) {
clearInterval(countdownTimer);
// document.qForm.submit();
} else {
seconds--;
}
minutes = Math.floor(seconds / 60);
document.getElementById('countdown').innerHTML = minutes+':'+ (seconds%60);
}
countdownTimer = setInterval(secondPassed, 1000);
You could use momentJS to convert the time into a duration (a little overkill though) and setting an end date with it, like so:
function convertStringToDuration( s ) {
// Just in case, if you plan to use hours as well in the future :)
const parameterNames = [ 'hours', 'minutes', 'seconds' ];
const parts = s.split( ':' );
// Generate moment.duration parameters
const parameters = parameterNames.slice( -parts.length ).reduce( ( result, param, index ) => {
result[ param ] = parseInt( parts[ index ], 10 );
return result;
}, {} );
return moment.duration( parameters );
}
function pass( timer, element, end, callback ) {
// get the current time
const now = moment();
// calculate duration
let duration = end - now;
if ( duration <= 0 ) {
duration = 0;
clearInterval( timer );
if ( typeof callback === 'function' ) {
callback();
}
}
// and format time
const formatted = moment.utc( duration ).format( 'm:ss' );
element.innerText = formatted;
}
function action() {
console.log( 'Done' );
}
const countdown = document.querySelector( '#countdown' );
const duration = convertStringToDuration( countdown.innerText );
// set end time
const end = moment().add( duration );
let countdownTimer = setInterval( () => pass( countdownTimer, countdown, end, action ), 250 );
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.0/moment.min.js"></script>
<h3>Question 1 of 20<br><time id="countdown">5:00</time></h3>
<form name="qForm">
<input type="radio"> 219
<input type="submit" value="Submit">
</form>
Related
I want my stopwatch as if 60 seconds complete . There should be an increment in minute and seconds to start from 0 again . I tried so many ways to do so but it always stop working when time is one minute .. is it built in problem in setInterval()
async timer() {
var timeout = setInterval(() => {
count++
this.timerSecond.innerText = count;
if (count > 59) {
count = 0;
this.timerSecond.innerText = count;
count1++
this.timerMinute.innerText = count1
}
}, 100);
console.log(timeout);
return timeout;
}
Does this method work for you?
timer () {
let seconds = 0;
const tick = () => {
this.timerText.textContent = seconds;
this.timerSecond.textContent = `${seconds % 60}`.padStart(2, '0');
this.timerMinute.textContent = Math.floor(seconds / 60);
seconds++;
};
tick();
return setInterval(tick, 1000);
}
It's hard to tell why you had two separate setInterval() calls, but I removed the one called every 100 milliseconds and combined the logic into a single one.
The timerSecond uses modulo 60 of seconds, and timerMinute uses result of integer division by 60, while the timerText just receives the seconds directly, as in your initial code.
The async keyword didn't add any value to your code, since none of it uses promises, so I removed it.
Here's a slightly elaborated example to demonstrate functionality:
class Stopwatch {
timerText = document.querySelector('.text');
timerSecond = document.querySelector('.second');
timerMinute = document.querySelector('.minute');
timer () {
let seconds = 0;
const tick = () => {
this.timerText.textContent = seconds;
this.timerSecond.textContent = `${seconds % 60}`.padStart(2, '0');
this.timerMinute.textContent = Math.floor(seconds / 60);
seconds++;
};
tick();
return setInterval(tick, 1000);
}
}
new Stopwatch().timer();
<div class="text"></div>
<div>
<span class="minute"></span>:<span class="second"></span>
</div>
This is nice and simple:
var seconds = 0;
setInterval(function(){
tick(document.getElementById("timer"), ++seconds);
}, 1000);
function tick(ele, secs){
ele.innerHTML = Math.floor(secs / 60) + ":" + (secs % 60 < 10 ? "0" : "") + secs % 60;
}
<span id="timer">0:00</span>
Math.floor(secs / 60) gives us the minutes and excludes any remainder of seconds, secs % 60 gives us the remainder of seconds after we've divided by 60 (so it essentially removes the minutes), and (secs % 60 < 10 ? "0" : "") gives us a leading "0" if the seconds (excluding whole minutes) is less than 10.
Here is a basic example of how to make a counter that counts down from 60 (or any other number) and display it on the page.
// Reference to html element that holds the count
const counterDiv = document.getElementById('counter');
// Variable that will keep track of the seconds to count down from
const secondsToCount = 60;
// Set the initial value of the counter to the secondsToCount variable.
// The counter will be updated each 1000ms | 1s and will be changed
// to the value of the remaining seconds to count down from
// which is why this variable is let opposed to const
let counter = secondsToCount;
// Set the initial text of the counter html element
counterDiv.innerHTML = secondsToCount;
// Function that is going to do counting for us.
const interval = setInterval( () => {
// Decrement the current counter value by 1
counter--;
// Update the text to show the new value of our counter
counterDiv.innerHTML = counter;
// If the counter is === 0, the counter is finished. She some text.
if (counter === 0) {
// Clear the interval (otherwise, you'll continue to count
// into the negative numbers
clearInterval(interval);
// Show the completed text
counterDiv.innerHTML = `Done counting down from ${secondsToCount} seconds`;
}
}, 1000); // 1000 ms | 1s
<!-- Element used to show the count -->
<div id="counter"></div>
Here's the scenario, I have a time that counts the time_taken by a user. What I want is to get the exact time_taken based from the timer. For example, a user take an exam, then after he/she take the exam, the time_taken will be submitted (e.g. 1hr 25mins 23secs). Please see my code below.
$(document).ready(function(){
var d;
setInterval(function(){
d = new Date();
dates = d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds();
$('#timeTaken').val(dates);
}, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="timeTaken" value="">
Here is Fiddle for the solution
https://jsfiddle.net/djzsddz6/1/
Ans Solution is below:
$(document).ready(function(){
var seconds = 0, minutes = 0 , hours = 0;
setInterval(function(){
seconds++;
if(seconds == 60){
minutes++
seconds = 0;
}
if(minutes == 60){
hours++
minutes = 0;
}
console.log(hours, minutes, seconds);
$('#timeTaken').val(`${hours}:${minutes}:${seconds}`);
}, 1000);
});
I don't really see the point to use an input there, you can just display in a span and when the form gets submitted take the time elapsed and send it with other data. Anyways, this should work for you:
$(document).ready(function () {
var time_start = new Date();
setInterval(function () {
var time_end = new Date();
var time_diff = (time_end - time_start);
// hours
var hours = Math.floor(time_diff / 1000 / 60 / 60);
// minutes
time_diff = time_diff - hours * 1000 * 60 * 60;
var minutes = Math.floor(time_diff / 1000 / 60);
// seconds
time_diff = time_diff - minutes * 1000 * 60;
var seconds = Math.floor(time_diff / 1000);
renderTime(hours, minutes, seconds);
}, 1000);
});
function renderTime (hrs, min, sec) {
var str = convertTime(hrs) + ":" + convertTime(min) + ":" + convertTime(sec);
$("#timeTaken").val(str);
}
function convertTime (val) {
return val < 10 ? "0" + val : val;
}
What's going on here is we have the time_start which does not change and we have setInterval function that is triggered every second. There we create new Date object, and the subtract the static one from it, which returns the time difference in milliseconds. We do the weird Math.flooring and subtracting, so we can have hours, minutes and seconds as an integers (not floats). Then we use render function to display the time inside an desired element.
Why I think it's a better solution then the others are, is that if you want to handle the user's page refresh you just need to save one variable to cookie or something else and it will work regardless of the page refresh.
Handling the page refresh would look like (with cookie saved for 2 hrs):
function updateTimeCookie () {
var time_now = new Date()
var value = JSON.stringify(time_now);
var expires = time_now.setTime(time_now.getTime() + 7200);
$.cookie("timeStart", value, { expires: expires });
};
// to get Date object from cookie: new Date(JSON.parse($.cookie("timeStart")))
To use $.cookie() you must first include jQuery Cookie Plugin.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
Working fiddle without cookie: https://jsfiddle.net/qc3axmf4/1/
Working fiddle with cookie: https://jsfiddle.net/ta8bnzs0/2/
Rather than getting date at every second you can keep the counter in set interval which will updated at every second. At the time of submission you can perform division and modulus operation to get exact time taken
Example
$(document).ready(function(){
var timer =0;
setInterval(function(){
Timer +=1;
// Code for display in hr mm and ss
$('#timeTaken').val(dates);
}, 1000'
});
You can also convert second in time valueby using moment.js
Hope this helps you.
Happy coding
I have an HTML Table used to generate a Calendar which shows TimeClock entries for each day a user has worked and clocked in and out of the system. Each day also shows the total time duration for each clock in/out entry. (multiple "timecard punches" can be within the same day)
Simply put I have DateTime style strings which hold a Duration value and I need to add them all together to get a combined duration value.
In a loop this JavaScript variable totalTimeValue will be assigned a duration value as a text string like this 03:31:23
Add these duration strings together using JavaScript...
03:31:23
04:21:56
04:08:42
03:31:17
04:10:59
02:48:21
04:26:11
00:00:39
03:41:37
Using JavaScript and jQuery I have this code below which gets the DateTime Duration value as a string in the format 04:21:19 for each timeclock entry.
My JavaScript/jQuery so far...
Demo of it working here: http://codepen.io/jasondavis/pen/GpPPPR?editors=101
var totalTimeValue = 0;
var totalWeekTimeValue = 0;
// itterate each week which is table row <tr>
$('#timeclock-cal > tbody > tr').each(function() {
console.log('=== New Week ===');
totalWeekTimeValue = 0;
// get each day which is a table cell <td>
$(this).find('td').each(function() {
console.log('== New Day ==');
// get total time val for each clock in/out on each day which is inside
// button with CSS class .cal-punch-total-time
$(this).find('.cal-punch-total-time').each(function() {
totalTimeValue = $(this).text();
console.log(totalTimeValue);
// THIS PART NEEDS YOUR HELP!
// NEED TO ADD EACH DATETIME STRING TOGETHER FOR TOTAL DURATION VALUES
totalWeekTimeValue = totalTimeValue+totalWeekTimeValue;
});
});
console.log('= total week time === '+totalWeekTimeValue);
});
full size image
I have no objection to using the MomentJS library http://momentjs.com/ if it can help in this situation however my research so far did not really show any examples doing what I need to do in this question.
In fact all my StackOverflow and Google searches resulted in no examples of adding durations like this in JavaScript!
I did find this MomentJS plugin MomentJS Durations - https://github.com/jsmreese/moment-duration-format
With JQuery and Javascript its easily possible. Please have a look at below code.
$(document).ready(function(){
var pad = function(num) { return ("0"+num).slice(-2); }
var totalSeconds = 0;
$("li").each(function(){
var currentDuration = $(this).text();
currentDuration = currentDuration.split(":");
var hrs = parseInt(currentDuration[0],10);
var min = parseInt(currentDuration[1],10);
var sec = parseInt(currentDuration[2],10);
var currDurationSec = sec + (60*min) + (60*60*hrs);
totalSeconds +=currDurationSec;
});
var hours = Math.floor(totalSeconds / 3600);
totalSeconds %= 3600;
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
$(".totalVal").text(pad(hours)+":"+pad(minutes)+":"+pad(seconds));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul>
<li>03:31:23</li>
<li>04:21:56</li>
<li>04:08:42</li>
<li>03:31:17</li>
<li>04:10:59</li>
<li>02:48:21</li>
<li>04:26:11</li>
<li>00:00:39</li>
<li>03:41:37</li>
</ul>
<div id="totalTime">Total Time:<span class="totalVal"></span></div>
This is a little overkill, but shows how to use map and reduce.
/** Calculate the number of seconds from HH:MM:SS **/
function getSeconds(time) {
var parts = time.split(":");
return parseInt(parts[0], 10) * 3600 + parseInt(parts[1], 10) * 60 + parseInt(parts[2], 10);
}
//select all the elements
var totalSeconds = $("a.cal-punch-total-time")
.map( function(ind, elem) { //convert the jQuery object into the array
var text = $(elem).text(); //get the text from the anchor
return getSeconds(text); //set the index to the total seconds
})
.get() //gets the array out of the jQuery object
.reduce( function(runningTotal, currentValue){ //Now to combine all the values into one
return runningTotal + currentValue; //sum up the values
},0); //The initial starting vaule
//Now get the hour, minutes, and seconds from the total seconds
var hours = parseInt( totalSeconds / 3600 );
var minutes = parseInt( totalSeconds / 60 ) % 60;
var seconds = totalSeconds % 60;
//left pad numbers less than ten
if(hours<10) hours = "0" + hours;
if(minutes<10) minutes = "0" + minutes;
if(seconds<10) seconds = "0" + seconds;
$("#out").html("Total Time: " + (hours + ":" + minutes + ":" + seconds));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a class="cal-punch-total-time">03:31:23</a>
<a class="cal-punch-total-time">04:21:56</a>
<a class="cal-punch-total-time">04:08:42</a>
<a class="cal-punch-total-time">03:31:17</a>
<a class="cal-punch-total-time">04:10:59</a>
<a class="cal-punch-total-time">02:48:21</a>
<a class="cal-punch-total-time">04:26:11</a>
<a class="cal-punch-total-time">00:00:39</a>
<a class="cal-punch-total-time">03:41:37</a>
<div id="out"></div>
Try this:
function hhmmssToSeconds(str) {
var arr = str.split(':').map(Number);
return (arr[0] * 3600) + (arr[1] * 60) + arr[2];
};
function secondsToHHMMSS(seconds) {
var hours = parseInt(seconds / 3600, 10),
minutes = parseInt((seconds / 60) % 60, 10),
seconds = parseInt(seconds % 3600 % 60, 10);
return [hours, minutes, seconds].map(function (i) { return i.toString().length === 2 ? i : '0' + i; }).join(':');
}
Then:
var t1 = hhmmssToSeconds('40:50:40'),
t2 = hhmmssToSeconds('04:12:30');
var result = secondsToHHMMSS(t1 + t2); // '45:03:10'
You can split your DateTime strings into arrays.
totalTimeValue = $(this).text().split(':'); //from '03:10:30' to ['03','10','30']
Then loop through your array and coerce each string to a number.
totalTimeValue.forEach(function(val,idx){
totalWeekTimeValue[idx] += Number(val);
});
This will leave you with an array of values for totalWeekTime that you can format/recalculate and rejoin if needed.
totalWeekTimeValue.join(':'); //[3:10:30]
http://codepen.io/hirechrismeyers/pen/ZbVVgr
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.
I have a countdown like this one:
var countdown = {
startInterval: function() {
var count = 600
var countorig = 600;
var currentId = setInterval(function() {
var min = (count - (count % 60)) / 60;
var sec = count % 60;
if (sec < 10) {
$('#timer').html(min + ':0' + sec);
} else {
$('#timer').html(min + ':' + sec);
}
$('#time').val(countorig - count);
if (count == 0) {
$('#form').submit();
}--count;
}, 1000);
countdown.intervalId = currentId;
}
};
It works. But if I load the page, the countdown starts but it stutter it is not "round" like a clock is.
JSFiddle.
setInterval isn’t exact. You should use Dates instead, to get an accurate time, and then choose an interval of less than one second to get a smoother clock. Here’s a demo!
var countdown = {
startInterval: function() {
var count = 600;
var start = new Date(); // The current date!
var currentId = setInterval(function() {
var difference = Math.max(0, count - (new Date() - start) / 1000 | 0);
var min = difference / 60 | 0;
var sec = difference % 60;
$('#timer').text(min + ':' + (sec < 10 ? '0' : '') + sec);
$('#time').val(difference);
if(count === 0) {
$('#form').submit();
}
}, 200);
countdown.intervalId = currentId;
}
};
It's never a good idea to assume your timers are exact. Instead, use delta timing.
var startTime = new Date().getTime();
setInterval(function() {
var elapsed = new Date().getTime()-startTime;
console.log("Been running for "+Math.floor(elapsed/1000)+" seconds");
},25);
That is because setInterval is not meant to be a high resolution timer. It will NOT hit every 1000 milliseconds on the dot. You might have swings as much as 20 to 30 milliseconds in either direction, resulting in a clock that is off.
Using Date.now(), this is a quick example of a countdown function ( x is milliseconds )
function countdown(x){
var o = {future: Date.now()+x, last:-1, i:null}; // object so we can pass by-ref if req.
o.i = setInterval( function() { // use o.i so we can clear interval
var remain = o.future - Date.now(),
secs = Math.floor( remain / 1000 ),
mins = 0;
if( remain < 0 ){ // finished, do whatever
return clearInterval(o.i); // then clear & exit
}
if( secs === o.last ) return; // skip this iteration if it's not been a second
o.last = secs; // else update last time
// do whatever you want for this new second
if( secs > 59 ) mins = Math.floor( secs / 60 ), secs = secs % 60;
console.log(
(mins < 10 ? '0'+mins : mins) + ':' +
(secs < 10 ? '0'+secs : secs) + ' remain.'
);
}, 100);
}
If you know it wont be used in IE, consider adding o as an argument to the callback function in the interval and also as the last argument to setInterval (so it is passed to the callback as first arg), which means closure is independent => callback can be defined anywhere.