Please see code below. Timer works, but minutes decreases faster than the seconds. Minutes shouldn't be decreasing before seconds reaches 0. How do I make it so that minutes decreases AFTER seconds reach 0?
(function($) {
function timer(time, update, complete) {
var start = new Date().getTime();
var interval = setInterval(function() {
var now = time - (new Date().getTime() - start);
if (now <= 0) {
clearInterval(interval);
complete();
} else update(Math.floor(now / 1000));
}, 0);
}
timer(
300000,
function(timeleft) {
var min = Math.round(timeleft / 60);
var sec = Math.round(timeleft / 5);
$('.timer').html(min + " minutes " + sec + " seconds");
}
);
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class=timer></span>
If you set your timer say at one minute, you cannot start at 59 seconds.
Use setTimeout instead of setInterval
Calculate seconds using timeInSeconds % 60
Don't set timers (setTimeout in our case) to 0. Use something performance-friendly like 1000 / 60
(function($) {
/**
* timer - Countdown seconds from a provided ms value
* #param {Number} time - time in MS
* #param {function} update - Callback - returns time in seconds
* #param {function} complete - Callback - returns time in seconds on complete
*/
function timer(time, update, complete) {
var start = +new Date(),
timeout, now, sec;
(function tick() {
now = time - (+new Date() - start);
sec = Math.ceil(now / 1000);
if (now <= 0) {
// STOP ticking!
clearTimeout(timeout);
// COMPLETE - Execute callback function (if provided)
if (complete && typeof complete === "function") complete(sec);
} else {
timeout = setTimeout(tick, 1000 / 60); // Recursive ticks...
}
// UPDATE - Execute callback function (if provided)
if (update && typeof update === "function") update(sec);
})(); // start ticking... (Thank you Timeout!)
}
timer(
60000,
function(timeInSeconds) {
var min = Math.floor(timeInSeconds / 60),
sec = timeInSeconds % 60;
$('.timer').html(min + " minutes " + sec + " seconds");
}, function(timeInSeconds) {
console.log("DONE! " + timeInSeconds);
}
);
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class=timer></span>
I think your problem is about Math.round(), what about using ceil instead of it ?
EDIT : Maybe you also want substract by 1 because when timeleft is 59 or less , minute become still 1.
(function($) {
function timer(time, update, complete) {
var start = new Date().getTime();
var interval = setInterval(function() {
var now = time - (new Date().getTime() - start);
if (now <= 0) {
clearInterval(interval);
complete();
} else update(Math.floor(now / 1000));
}, 0);
}
timer(
300000,
function(timeleft) {
var min = Math.ceil(timeleft / 60 ) - 1;
var sec = Math.round(timeleft / 5);
$('.timer').html(min + " minutes " + sec + " seconds");
}
);
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class=timer></span>
Related
I want to make 3 countdown timers where the next one starts when the last one ends (the first timer will start counting down the time automatically, but the second timer will only start when the first one reaches 0:00 and the third one will only start when the second one reaches 0:00).
I found this code for a countdown timer:
function countDown() {
var seconds = 60;
var mins = 5;
function clickClock() {
var counter = document.getElementById("countdown1");
var currentMinutes = mins - 1;
seconds--;
counter.innerHTML = currentMinutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
if(seconds > 0) {
setTimeout(clickClock, 1000);
} else {
if(mins > 1) {
countDown(mins-1);
}
}
}
clickClock();
}
countDown();
On my HTML, I have 3 spans, each with a unique ID (#countdown1, #countdown2, #countdown3)
I have tried passing in an parameter to the clickClock() function called counter so that whenever I called the function I could enter the id of the element I wanted to affect, didn't work.
I could just make 2 other functions that would do exactly the same thing but would change the counter variable, but I'd like to avoid repeating unnecessary things in my code.
How could this be done?
Any help is appreciated :)
I'd do something like this:
/////////// USAGE
const timersDurationInMilliseconds = 1000 * 5; // for 5 minutes do: 1000 * 60 * 5
// Render initial timer content
RenderTimer('countdown1', timersDurationInMilliseconds);
RenderTimer('countdown2', timersDurationInMilliseconds);
RenderTimer('countdown3', timersDurationInMilliseconds);
// Start countdown, then start another ones
countDown(timersDurationInMilliseconds, 'countdown1')
.then(() => countDown(timersDurationInMilliseconds, 'countdown2'))
.then(() => countDown(timersDurationInMilliseconds, 'countdown3'))
.then(() => alert('All timers finished!'));
/////////// REQUIRED METHODS
function countDown(durationInMilliseconds, elementId) {
return new Promise((resolve, reject) => {
const updateFrequencyInMilliseconds = 10;
const currentTimeInMilliseconds = new Date().getTime();
const endTime = new Date(currentTimeInMilliseconds + durationInMilliseconds);
function updateTimer(elementId) {
let timeLeft = endTime - new Date();
if (timeLeft > 0) {
// We're not done yet!
setTimeout(updateTimer, updateFrequencyInMilliseconds, elementId);
} else {
// Timer has finished!
resolve();
// depending on update frequency, timer may lag behind and stop few milliseconds too late
// this will cause timeLeft to be less than 0
// let's reset it back to 0, so it renders nicely on the page
timeLeft = 0;
}
RenderTimer(elementId, timeLeft);
}
updateTimer(elementId);
});
}
function padNumber(number, length) {
return new String(number).padStart(length || 2, '0'); // adds leading zero when needed
}
function RenderTimer(elementId, timeLeft) {
const hoursLeft = Math.floor(timeLeft / 1000 / 60 / 60 % 60);
const minutesLeft = Math.floor(timeLeft / 1000 / 60 % 60);
const secondsLeft = Math.floor(timeLeft / 1000 % 60);
const millisecondsLeft = timeLeft % 1000;
const counterElement = document.getElementById(elementId);
counterElement.innerHTML = `${padNumber(hoursLeft)}:${padNumber(minutesLeft)}:${padNumber(secondsLeft)}.${padNumber(millisecondsLeft, 3)}`;
}
<p>First countdown: <span id="countdown1"></span></p>
<p>Second countdown: <span id="countdown2"></span></p>
<p>Third countdown: <span id="countdown3"></span></p>
If you want to only display minutes and seconds, you can adjust that behavior in RenderTimer method.
If you don't plan on displaying milliseconds to the user, you may want to change how frequent the timer is updated and rendered on the page by adjusting the updateFrequencyInMilliseconds variable (e.g. from 10ms to 1000ms).
I'm having a problem get this countdown timer to stop at zero so the time won't show as a negative value. The console.log is getting called and works fine but for some reason the clearInterval() is not. This is driving me crazy and I'm close to quitting.
const timerContainer = document.getElementById('timerContainer');
const THREEMINUTES = 60 * 0.1;//5 seconds for testing
startTimer(THREEMINUTES, timerContainer);
function startTimer(duration, display) {
let start = Date.now();
let diff, min, sec;
let timer = () => {
diff = duration - (((Date.now() - start) / 1000) | 0);
//use bitwise to truncate the float
min = (diff / 60) | 0;
sec = (diff % 60) | 0;
min = min < 10 ? '0' + min : min;
sec = sec < 10 ? '0' + sec : sec;
display.textContent = min + ':' + sec;
if (diff <= 0) {
stopTimer();
submit.disabled = 'true';
};
};
//call timer immediately otherwise we wait a full second
timer();
setInterval(timer, 1000);
function stopTimer() {
clearInterval(timer);
console.log("time's up", diff);
};
}
<div id="timerContainer"></div>
You are not saving the result of setInterval(timer, 1000);
you should use this:
let timerId;
timer();
timerId = setInterval(timer, 1000);
function stopTimer() {
clearInterval(timerId);
console.log("time's up", diff)
};
As you might see, the result of setInterval is a number (object in node), and all you then need to do is pass that value to clearInterval thus we save the value in the variable timerId for reference.
Don't pass the function that you want stopped to clearInterval().
Pass a reference to the timer that you started, so you need to make sure that when you start a timer, you capture a reference to the ID that will be returned from it.
// Function that the timer will invoke
function callback(){
. . .
}
// Set up and initiate a timer and capture a reference to its unique ID
var timerID = setInterval(callback, 1000);
// When needed, cancel the timer by passing the reference to it
clearInterval(timerID);
The code is fixed make sure you fix your submit button code.
You should first assign the value of setInterval to a variable. That variable is used while calling clearInterval which infact clears the interval.
const timerContainer = document.getElementById('timerContainer');
const THREEMINUTES = 60 * 0.1;//5 seconds for testing
startTimer(THREEMINUTES, timerContainer);
var timer = null;
function startTimer(duration, display) {
let start = Date.now();
let diff, min, sec;
let timer = () => {
diff = duration - (((Date.now() - start) / 1000) | 0);
//use bitwise to truncate the float
min = (diff / 60) | 0;
sec = (diff % 60) | 0;
min = min < 10 ? '0' + min : min;
sec = sec < 10 ? '0' + sec : sec;
display.textContent = min + ':' + sec;
if (diff <= 0) {
stopTimer();
submit.disabled = 'true';
};
};
//call timer immediately otherwise we wait a full second
timer();
timer = setInterval(timer, 1000);
function stopTimer() {
clearInterval(timer);
console.log("time's up", diff);
};
}
I've been working on a Javascript pomodoro clock. I am able to set the session time and break time and it counts down without any trouble. But for some reason I can not get pause and resume to work. When the timer starts I capture the Date.now() and when I pause it I capture the current Date.now(). I find the difference and subtract from the duration, hoping to resume at the paused time, but it still keeps subtracting additional seconds. My code (from codepen) is below
$(document).ready(function() {
var total;
var i;
var x;
var y;
var display;
var minutes;
var seconds;
var duration;
var sessionInterval;
var freeze;
var timePast;
var t;
var start;
var clock;
function timer(end) {
total = Date.parse(end) - Date.parse(new Date());
minutes = Math.floor((total / 1000 / 60) % 60);
seconds = Math.floor((total / 1000) % 60);
return {
'total': total,
'minutes': minutes,
'seconds': seconds
};
}
function beginTimer() {
start = Date.now();
clearInterval(sessionInterval);
clock = document.getElementById('display2');
start = Date.now();
sessionInterval = setInterval(function() {
t = timer(duration);
clock.innerHTML = 'minutes:' + t.minutes + '<br>' + 'seconds:' + t.seconds + '<br>';
if (t.total <= 0) {
clearInterval(sessionInterval);
if (i === 0) {
session();
} else if (i === 1) {
breakTime();
}
}
}, 1000);
}
function session() {
duration = new Date(Date.parse(new Date()) + (x * 60 * 1000));
beginTimer();
i = 1;
}
function breakTime() {
duration = new Date(Date.parse(new Date()) + (y * 60 * 1000));
beginTimer();
i = 0;
}
$(".sendInput").click(function() {
if (x == null) {
x = 25;
} else {
x = parseInt(document.getElementById("workTime").value, 10);
}
if (y == null) {
y = 5;
} else {
y = parseInt(document.getElementById("breakMin").value, 10);
}
session();
});
$(".sendPause").click(function() {
freeze = Date.now();
timePast = freeze - start;
clearInterval(sessionInterval);
});
$(".sendResume").click(function() {
if (i === 1) {
duration = new Date(((Date.parse(new Date())) + (x * 60 * 1000)) - timePast);
}
if (i === 0) {
duration = new Date(((Date.parse(new Date())) + (y * 60 * 1000)) + timePast);
}
beginTimer();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" placeholder="break: 5 minutes" id="breakMin">
<input type ="text" placeholder="session: 25 minutes" id="workTime">
<input type="button" value="Start" class="sendInput">
<input type="button" value="Pause" class="sendPause">
<input type="button" value="Resume" class="sendResume">
<div id="display2">
</div>
The major logic problem is within the resume function which does not reset start to a new notional value that is timePast milliseconds before the present. Using the original start value after a pause of undetermined duration simply does not work.
Date.parse(new Date()) also appeared to be causing problems. Without spending time on debugging it further, all occurrences of Date.parse(new Date()) were simply replaced with Date.now().
So a slightly cleaned up version of the resume function that appears to work:
$(".sendResume").click(function() {
var now = Date.now();
if (i === 1) {
duration = new Date( now + x * 60 * 1000 - timePast);
}
if (i === 0) {
duration = new Date( now + y * 60 * 1000 + timePast);
}
beginTimer();
start = now - timePast; // <-- reset notional start time
});
but please test it further - you may wish to investigate why timePast is added in one calculation of duration and subtracted in the other!
i want this my javascript code to to be able to be reading 3 hours countdown and also redirect to a new page after the countdown is complete
<script type="text/javascript">
// properties
var count = 0;
var counter = null;
window.onload = function() {
initCounter();
};
function initCounter() {
// get count from localStorage, or set to initial value of 1000
count = getLocalStorage('count') || 1000;
counter = setInterval(timer, 1000); //1000 will run it every 1 second
}
function setLocalStorage(key, val) {
if (window.localStorage) {
window.localStorage.setItem(key, val);
}
return val;
}
function getLocalStorage(key) {
return window.localStorage ? window.localStorage.getItem(key) : '';
}
function timer() {
count = setLocalStorage('count', count - 1);
if (count == -1) {
clearInterval(counter);
return;
}
var seconds = count % 60;
var minutes = Math.floor(count / 60);
var hours = Math.floor(minutes / 60);
minutes %= 60;
hours %= 60;
document.getElementById("timer").innerHTML = hours + "hours " + minutes + "minutes and " + seconds + " seconds left to complete this transaction"; // watch for spelling
}
</script>
<div id="timer"></div>
please help me make it better by making it been able to countdown to three hour and also redirect to another page after the countdown is complete
You didn't properly set total time. You set it to 16 minutes instead of 3 hours. Here is the working code (try it on JSFiddle):
var time = 60 * 60 * 3;
var div = document.getElementById("timer");
var t = Date.now();
var loop = function(){
var dt = (Date.now() - t) * 1e-3;
if(dt > time){
doWhateverHere();
}else{
dt = time - dt;
div.innerHTML = `Hours: ${dt / 3600 | 0}, Minutes: ${dt / 60 % 60 | 0}, Seconds: ${dt % 60 | 0}`;
}
requestAnimationFrame(loop);
};
loop();
Also, do not use setInterval and setTimeout for precise timing. These functions are volatile. Use Date.now() instead.
I have the following lines of code on my web page - example/demo.
HTML:
<p class="countdown-timer">10:00</p>
<p class="countdown-timer">10:00</p>
JavaScript:
function startTimer(duration, display) {
var start = Date.now(),
diff,
minutes,
seconds;
function timer() {
// get the number of seconds that have elapsed since
// startTimer() was called
diff = duration - (((Date.now() - start) / 1000) | 0);
// does the same job as parseInt truncates the float
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) {
// add one second so that the count down starts at the full duration
// example 05:00 not 04:59
start = Date.now() + 1000;
}
};
// we don't want to wait a full second before the timer starts
timer();
setInterval(timer, 1000);
}
$(document).ready(function(){
// set the time (60 seconds times the amount of minutes)
var tenMinutes = 60 * 10,
display = document.querySelector('.countdown-timer');
startTimer(tenMinutes, display);
});
As I'm relatively new to JavaScript/jQuery, how would I be able to make the timer stop on 0 and so that the second clock also works?
I have tried replacing document.querySelector('.countdown-timer'); with $('.countdown-timer');
I created a class to do that a while ago, for one of my projects. It allows you to have multiple counters, with different settings. It can also be configured to be paused or reset with a button, using the available functions. Have a look at how it's done, it might give you some hints:
/******************
* STOPWATCH CLASS
*****************/
function Stopwatch(config) {
// If no config is passed, create an empty set
config = config || {};
// Set the options (passed or default)
this.element = config.element || {};
this.previousTime = config.previousTime || new Date().getTime();
this.paused = config.paused && true;
this.elapsed = config.elapsed || 0;
this.countingUp = config.countingUp && true;
this.timeLimit = config.timeLimit || (this.countingUp ? 60 * 10 : 0);
this.updateRate = config.updateRate || 100;
this.onTimeUp = config.onTimeUp || function() {
this.stop();
};
this.onTimeUpdate = config.onTimeUpdate || function() {
console.log(this.elapsed)
};
if (!this.paused) {
this.start();
}
}
Stopwatch.prototype.start = function() {
// Unlock the timer
this.paused = false;
// Update the current time
this.previousTime = new Date().getTime();
// Launch the counter
this.keepCounting();
};
Stopwatch.prototype.keepCounting = function() {
// Lock the timer if paused
if (this.paused) {
return true;
}
// Get the current time
var now = new Date().getTime();
// Calculate the time difference from last check and add/substract it to 'elapsed'
var diff = (now - this.previousTime);
if (!this.countingUp) {
diff = -diff;
}
this.elapsed = this.elapsed + diff;
// Update the time
this.previousTime = now;
// Execute the callback for the update
this.onTimeUpdate();
// If we hit the time limit, stop and execute the callback for time up
if ((this.elapsed >= this.timeLimit && this.countingUp) || (this.elapsed <= this.timeLimit && !this.countingUp)) {
this.stop();
this.onTimeUp();
return true;
}
// Execute that again in 'updateRate' milliseconds
var that = this;
setTimeout(function() {
that.keepCounting();
}, this.updateRate);
};
Stopwatch.prototype.stop = function() {
// Change the status
this.paused = true;
};
/******************
* MAIN SCRIPT
*****************/
$(document).ready(function() {
/*
* First example, producing 2 identical counters (countdowns)
*/
$('.countdown-timer').each(function() {
var stopwatch = new Stopwatch({
'element': $(this), // DOM element
'paused': false, // Status
'elapsed': 1000 * 60 * 10, // Current time in milliseconds
'countingUp': false, // Counting up or down
'timeLimit': 0, // Time limit in milliseconds
'updateRate': 100, // Update rate, in milliseconds
'onTimeUp': function() { // onTimeUp callback
this.stop();
$(this.element).html('Go home, it\'s closing time.');
},
'onTimeUpdate': function() { // onTimeUpdate callback
var t = this.elapsed,
h = ('0' + Math.floor(t / 3600000)).slice(-2),
m = ('0' + Math.floor(t % 3600000 / 60000)).slice(-2),
s = ('0' + Math.floor(t % 60000 / 1000)).slice(-2);
var formattedTime = h + ':' + m + ':' + s;
$(this.element).html(formattedTime);
}
});
});
/*
* Second example, producing 1 counter (counting up to 6 seconds)
*/
var stopwatch = new Stopwatch({
'element': $('.countdown-timer-up'),// DOM element
'paused': false, // Status
'elapsed': 0, // Current time in milliseconds
'countingUp': true, // Counting up or down
'timeLimit': 1000 * 6, // Time limit in milliseconds
'updateRate': 100, // Update rate, in milliseconds
'onTimeUp': function() { // onTimeUp callback
this.stop();
$(this.element).html('Countdown finished!');
},
'onTimeUpdate': function() { // onTimeUpdate callback
var t = this.elapsed,
h = ('0' + Math.floor(t / 3600000)).slice(-2),
m = ('0' + Math.floor(t % 3600000 / 60000)).slice(-2),
s = ('0' + Math.floor(t % 60000 / 1000)).slice(-2);
var formattedTime = h + ':' + m + ':' + s;
$(this.element).html(formattedTime);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
These 2 timers should count down from 10 minutes to 0 seconds:
<p class="countdown-timer">00:10:00</p>
<p class="countdown-timer">00:10:00</p>
But this one will count from 0 to 6 seconds:
<p class="countdown-timer-up">00:00:00</p>
I think your problem is you are passing an array into the startTimer function so it is just doing it for the first item.
If you change the document ready so that you initiate a timer for each instance of .countdown-timer, it should work:
// set the time (60 seconds times the amount of minutes)
var tenMinutes = 60 * 10;
$('.countdown-timer').each(function () {
startTimer(tenMinutes, this);
});
Example
document.querySelector('.class') will only find first element with .class. If you're already using jQuery I would recommend to do this:
var display = $('.countdown-timer');
for (var i = 0; i < display.length; i++) {
startTimer(tenMinutes, display[i]);
}
This way it will work for any number of countdown timers.
Here we go, jsfiddle
just changed the querySelector to getElementsByClassName to get all p elements with the same class. You can than start your timer on the different elements by using it's index.
No need for a queue :D
$(document).ready(function(){
// set the time (60 seconds times the amount of minutes)
var tenMinutes = 60 * 10,
display = document.getElementsByClassName('countdown-timer');
startTimer(tenMinutes, display[0]);
startTimer(tenMinutes, display[1]);
});