I have a web page where I entered a countdown code and it works well. However, I can only call it 1 time with the id in html.
Second or third time, it no longer works. How can I always use it? I would need it 3 or 4 times on the page. Thank you.
This is the code:
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 + " ore " + minutes + " min " + seconds + " sec"; // watch for spelling
}
You need to encapsulate the functionality in a way that can be called many times. It usually helps to think about the question "As a programmer, how I'd like to use this?"
Usually, it's a single function that takes some parameters and does something and/or returns something.
As an example... wouldn't it be nice if we had a function startMyTimer(...) that takes a timerId, and an element and sets up a timer that will update that element? We already have the signature:
function startMyTimer(timerId, element) { ... }
And now you can build everything inside this function. JS allows declaring functions within functions, which helps with encapsulation, so copying from your code it would look like:
function startMyTimer(timerId, element) {
var count = getLocalStorage(timerId) || 1000;
var counter = setInterval(timer, 1000); //1000 will run it every 1 second
function timer() {
count = setLocalStorage(timerId, 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;
element.innerHTML = hours + " ore " + minutes + " min " + seconds + " sec"; // watch for spelling
}
}
Note that now count and counter are both private to the scope of startMyTimer, so only within this function (and any function inside this one, such as function timer()) will see these variables.
So if you want to do exactly what you did, you'd use this function as
window.onload = function() {
startMyTimer('count', document.getElementById("timer"));
};
Again, this is just an example of a posible solution - Maybe you could pass in the element id instead of the element, or a timer duration, etc., and the best solution is the one that fits best your needs.
Related
I have a countdown in js and I can't add a trick I would like.
When the counting ends, it does not stop. Negative numbers start and instead I would like it to stop at 0 once the time has expired. How can I?
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("countdown").innerHTML = hours + " ore " + minutes + " min " + seconds + " sec";
}
The problem is that you were putting count with -1 value in LocalStorage.
count = setLocalStorage('count', count - 1);
And after page reload you kept subtracting 1 from -1 and you got -2, which your condition count == -1 couldn't catch. Solution is to put next count value in LocalStorage after you check if need to continue your timer or not.
<script type="text/javascript">
let count = 0;
let counter = null;
window.onload = function() {
initCounter();
};
function initCounter() {
// get count from localStorage, or set to initial value of 1000
count = Number(getLocalStorage('count')) || 5;
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() {
const nextCount = count - 1
if (nextCount < 0) {
clearInterval(counter);
return;
}
count = setLocalStorage('count', nextCount);
const seconds = count % 60;
let minutes = Math.floor(count / 60);
let hours = Math.floor(minutes / 60);
minutes %= 60;
hours %= 60;
document.getElementById("timer").innerHTML = hours + " ore " + minutes + " min " + seconds + " sec";
}
</script>
<div id="timer"></div>
Hope it helps :)
Change this:
if (count == -1) {
clearInterval(counter);
return;
}
To this:
if (count < 0) {
clearInterval(counter);
localStorage.removeItem('count');
return;
}
Always make your conditions as strict as you can, or you will run into trouble. You don't actually care that it's equal to -1. You care that it's below 0.
In your original code, it stops fine when the page is loaded without localStorage. But at the end, you set the localStorage to -1. When you refresh, you set it to -2 (count - 1) and start the counter going into the negatives. Your condition is never checked against that -1 value which was stored.
I made a timer that will reach zero.
and when it reaches zero make the timer run again.
the timer goes back to the starting number but doesnt run again.
also when i call it again the numbers just start to jump.
the code:
var timerPlace = document.getElementById('timer');
var timerP = document.getElementById('timerHard');
var stopTimer;
var toStop;
function timeMed() {
console.log('im in!')
var counter = 0;
var timeLeft = 5;
timerPlace.innerHTML = '00:45';
function timeIt() {
console.log('here')
counter++
timerPlace.innerHTML = convertSeconds(timeLeft - counter);
if (timerPlace.innerHTML == '00:00') {
clearInterval(stopTimer);
resetExercise();
timeMed();
}
}
function convertSeconds(s) {
var sec = s % 60;
var min = Math.floor((s % 3600) / 60);
return ('0' + min).slice(-2) + ':' + ('0' + sec).slice(-2);
}
if (!stopTimer) {
stopTimer = setInterval(timeIt, 1000);
}
}
You only call setInterval() when stopTimer is not set. But after the countdown completes, stopTimer is still set to the ID of the old interval timer, so you don't restart it. You should clear the variable when you call clearInterval().
if (timerPlace.innerHTML == '00:00') {
clearInterval(stopTimer);
stopTimer = null;
resetExercise();
timeMed();
}
Modern ES6 Approach and best practices.
I've decided to take the chance and refactor your code a little with Javascripts best practices in mind.
I've added comments which explain the code, and the engineering considerations.
The baseline for the timer is taken from the excellent answer here: https://stackoverflow.com/a/20618517/1194694
// Using destructuring on the paramters, so that the keys of our configuration object,
// will be available as separate parameters (avoiding something like options.duraitons and so on.
function startTimer({duration, onUpdate , infinite}) {
let timer = duration, minutes, seconds;
let interval = setInterval(function () {
minutes = parseInt(timer / 60);
seconds = parseInt(timer % 60);
// you can also add hours, days, weeks ewtc with similar logic
seconds = seconds < 10 ? `0${seconds}` : seconds;
minutes = minutes < 10 ? `0${minutes}` : minutes;
// calling your onUpdate function, passed from configuraiton with out data
onUpdate({minutes, seconds});
if (--timer < 0) {
// if infinite is true - reset the timer
if(infinite) {
timer = duration;
} else {
// Clearing the interval + additonal logic if you want
// I would also advocate implementing an onEnd function,
// So that you'll be able to decide what to do from configuraiton.
clearInterval(interval);
}
}
}, 1000);
}
const duration = 5;
const displayElement = document.querySelector("#timer");
startTimer({
duration,
onUpdate: ({minutes, seconds}) => {
// now you're not constraint to rendering it in an element,
// but can also Pass on the data, to let's say your analytics platform, or whatnot
displayElement.textContent = `${minutes}:${seconds}`;
},
infinite: true
});
<div id="timer">
</div>
I have made a memory game with a timer that start at 10 minutes and counts down every second to 0. This function is only called once and has the following setInterval function:
game.countdown = setInterval(function() {
if(game.timeLeft <= 0){
gameEnded = true;
}
if(!gameEnded){
// lower one second
game.timeLeft--;
var timespan = document.querySelector('.timeleft');
var minutes = pad(Math.floor((game.timeLeft / 60)),2 );
var newSeconds = pad(Math.floor(game.timeLeft - (minutes * 60)),2 );
function pad(num, size) {
var s = num+"";
while (s.length < size) s = "0" + s;
return s;
}
var niceTime = minutes + ":" + newSeconds;
timespan.innerHTML = niceTime;
// Then alter time bar on .innertimer
// Calculate percentage starting at 0%
var percentageDone = (Math.floor((10*60 - game.timeLeft) / (10*60) * 10000) / 100);
$(".timer").css({"background": "-webkit-linear-gradient(left, white " + percentageDone + "%, green " + percentageDone + "%)"});
}
}, 1000);
I store the setInterval in object game under variable name countdown. When the game is ended I call another function that has the clearInterval:
clearInterval(game.countdown);
Then when it goes to the next level it calls the setInterval function again and stores it again in game.countdown. But now for every second it takes 2 seconds. The next level 3 seconds. You can see there are multiple setInterval's at work because it's not done at the same time.
Hope someone can really help me out debugging this problem.
What I've done to work around the issue is the following. The interval is only being called if the following is true:
if (undefined == game.countdown){
This way if there is a countdown defined it's calling it again. And since I'm storing the game.timeLeft not in the function itself I can just refill game.timeLeft.
I am using timer in my project , I am having two problems in it. When start button is press time should start & when end button is press time should end.
But 1)when end button is clicked
time is not stopping .
2)when time decrease & reach 1 minute..time is stopped ..it should reduse in seconds also
var tim;
var min = 10;
var sec = 10;
var f = new Date();
function f1() {
f2();
document.getElementById("starttime").innerHTML = "Your started your Exam at " + f.getHours() + ":" + f.getMinutes();
}
function f2() {
if (parseInt(sec) > 0) {
sec = parseInt(sec) - 1;
document.getElementById("showtime").innerHTML = "Your Left Time is :" + min + " Minutes ," + sec + " Seconds";
tim = setTimeout("f2()", 1000);
} else {
if (parseInt(sec) == 0) {
min = parseInt(min) - 1;
if (parseInt(min) == 0) {
clearTimeout(tim);
location.href = ".././Home/Login";
} else {
sec = 60;
document.getElementById("showtime").innerHTML = "Your Left Time is :" + min + " Minutes ," + sec + " Seconds";
tim = setTimeout("f2()", 1000);
}
}
}
}
My suggestion would be this:
Fiddle
var tim;
var sec;
var f = new Date();
function f1() {
sec = 10 * 60;
f2();
document.getElementById("starttime").innerHTML = "Your started your Exam at " + f.getHours() + ":" + f.getMinutes();
}
function f2() {
if (sec > 0) {
sec--;
tim = setTimeout(f2, 1000);
} else {
document.getElementById("starttime").innerHTML = "times up!!!!";
}
var min = Math.floor(sec / 60);
var secDisplay = sec % 60;
document.getElementById("showtime").innerHTML = "Your Left Time is : " + min + " Minutes," + secDisplay + " Seconds";
}
function end() {
clearTimeout(tim);
sec = 0;
f2();
}
Changes are:
Removed the min variable and only use the total seconds instead of two variables. We can easily calculate the minutes based on the total seconds, for the display purposes. Using the seconds variable makes it much easier to check the time remaining. As you can see the code is simplified a lot.
Changed the setTimeout to take the function as an argument, not a string.
Added end() function, to be called when the end button is clicked.
Moved the seconds count to inside f1() so that it can be started and ended repeatedly without reloading the page.
Removed unnecessary parseInt() calls. The sec variable is already of numerical type so no need to convert it.
The timer will stop when the seconds reduce to 0. If you want it to stop when the time remaining reaches one minute, just change the condition in f2() to if (sec > 60) {
For your first question about the end button i cannot see any end button functionality. but you could have a stop function that clears the timeout.
function f3() {
clearTimeout(tim);
//update any text you wish here or move to the next page..
}
For your second question why it ends at 1 minute. You are decreasing the minute value before you check if it is zero. So when you come to f2, sec is 0 and min is 1 you then decrease min to 0.. And then check if it is zero and end the execution.
To solve that move your "min = parseInt(min) - 1;" to after the else and it should count the last minute too.
PS. you don't need parseint since you are using numbers allready
I have written a simple Javascript function (curteousy of codecall.net) that creates a count down timer.
It works fine when I run it, but I want to have more than one timer on the page.
When I place the function inside another div I get the numbers on the screen but only one of the last function actually counts down.
I have placed a link to the code here in JsFiddle which for one reason or another doesn't want to run it but it works. I just need multiple instances of it.
Any help is much appreciated, thanks in advance
The way you built it, all in the global namespace, makes it very difficult to incorporate two timers. Instead, you should just use a reusable object constructor. Demo here.
function Countdown(element, time) {
this.element = element;
this.time = time;
}
Countdown.time = function() {
return new Date().getTime() / 1000;
};
Countdown.formatRemaining = function(timeRemaining) {
function fillZero(n) {
return n < 10 ? '0' + n : n.toString();
}
var days = timeRemaining / 60 / 60 / 24 | 0;
var hours = timeRemaining / 60 / 60 | 0;
var minutes = timeRemaining / 60 | 0;
var seconds = timeRemaining | 0;
hours %= 24;
minutes %= 60;
seconds %= 60;
return days + ' day' + (days === 1 ? '' : 's') + ' ' + fillZero(hours) + ':' + fillZero(minutes) + ':' + fillZero(seconds);
};
Countdown.prototype.update = function() {
var timeRemaining = this.time + this.start - Countdown.time();
if(timeRemaining > 0) {
this.element.innerHTML = Countdown.formatRemaining(timeRemaining);
} else {
this.element.innerHTML = "Time's up!";
if(this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
};
Countdown.prototype.start = function() {
var countdown = this;
this.start = Countdown.time();
this.timer = setInterval(function() {
countdown.update();
}, 1000);
this.update();
};