I am trying to add functionality to a html/js stopwatch, but i have a problem with setTimeout() function. Why is it only executed once for me, but in this video:
https://www.youtube.com/watch?annotation_id=annotation_3059475893&feature=iv&src_vid=iSLWtGAw1Ic&v=gpFPppFU8s8
it is executed until someone stops it. Yeah I know I can use setInterval, but I'm just curious.
here is my code:
let running = false;
let time = 0;
function start() {
running = true;
timer();
}
function stop() {
running = false;
}
function reset() {
running = false;
time = 0;
document.getElementById("stw").innerHTML = "00:00:00";
}
function timer() {
if (running) {
setTimeout(function() {
time++;
let min = Math.floor(time / 100 / 60);
let sec = Math.floor(time / 100) % 60;
let mSec = time % 100;
if (min < 10) {
min = "0" + min;
}
if (sec < 10) {
sec = "0" + sec;
}
if (mSec < 10) {
mSec = "0" + mSec;
}
document.getElementById("stw").innerHTML =
min + ":" + sec + ":" + mSec;
}, 10);
}
}
When you use setTimeout() to create a loop, inside the function setTimeout() calls, you have to call setTimeout() again.
Here is a basic loop function that does it:
const loopFunc = () => {
console.log('loop');
setTimeout(loopFunc, 1000);
};
loopFunc(); // kick it off
In the video (around 2 minutes in), timer() calls itself recursively. Your code does not.
setTimeout() only calls its callback function once.
There are two ways to to have it call multiple times:
Use setInterval()
Use a recursive function to restart setTimeout() over and over again.
Example:
function timer() {
// do something
// call this function again in 10 milliseconds
setTimeout(timer, 10);
}
Related
So I'm trying to recreate a timer like the one in https://cstimer.net/ for the most part my code is working however when I try to reset the timer it does actually reset the time but the function is repeatedly called, I tested this by logging hello to the console how can I get this to stop as I plan for the page to record the timer result. Here is the code i tried to use to stop the reset function from reccuring.
function resetTime(){
minute.innerHTML = '00';
second.innerHTML = '00';
millisecond.innerHTML = '00';
if (millisecond.innerHTML == '00') {
console.log('hello');
cancelAnimationFrame(resetTime);
}
requestAnimationFrame(resetTime);
}
document.addEventListener('keypress', function(event) {
if (event.code === "KeyR") {
requestAnimationFrame(resetTime);
cancelAnimationFrame(timer);
return;
}
});
function timer() {
let ms = Date.now() - initialTime;
let sec = Math.floor(ms / 1000);
ms = ms % 100;
let min = Math.floor(sec / 60);
sec = sec % 60;
minute.innerHTML = min;
second.innerHTML = sec;
millisecond.innerHTML = ms;
requestAnimationFrame(timer);
}
having difficulty stopping timer outside of loop. I don't really know why the setTimeout() has been helping the function work... and i know its not the most syntactically correct.. but wondering if someone can help point me as to how to be able to call it outside the function to stop the countdown, say if an action occurs before the timer, and want to call a stopCountdown() function?
function countdown(start){
setTimeout(setCountdown, 1000);
let startingMinutes = timerEl.innerHTML;
startingMinutes = start;
let time = startingMinutes * 60;
function setCountdown(){
const minutes = Math.floor(time/60);
let seconds = time % 60;
if(seconds < 10){
seconds = '0' + seconds
} else {
seconds
}
if(minutes <=0 && seconds <=0){
clearInterval(start);
console.log('timerOver')
} else{
setTimeout(setCountdown, 1000);
timerEl.innerHTML = (minutes + ':'+seconds)
time--;
}
}}
function stopCountdown(){
document.querySelector("#countdown").innerText = '0'
setTimeout(setCountdown(start));
}
Welcome to coding, I am trying my best to explain it. First, let me point out some of my opinion on your code
function countdown(start){
setTimeout(setCountdown, 1000);
let startingMinutes = timerEl.innerHTML;
startingMinutes = start;
// I am not sure why you firstly initializing startMinutes
// to the value of timerEl.innerHTML
//and then reassign the value of startMinutes to variable start next line
let time = startingMinutes * 60;
function setCountdown(){
const minutes = Math.floor(time/60);
let seconds = time % 60;
if(seconds < 10){
seconds = '0' + seconds
} else {
seconds
}
if(minutes <=0 && seconds <=0){
clearInterval(start); // you are using setTimeout, not setInterval
console.log('timerOver')
} else{
setTimeout(setCountdown, 1000);
timerEl.innerHTML = (minutes + ':'+seconds)
time--;
}
}}
function stopCountdown(){
document.querySelector("#countdown").innerText = '0'
setTimeout(setCountdown(start));
// when calling stopCountdown(), what is the value of start?
// you can't access function setCountdown inside function stopCountdown
}
If my guess is correct, you want to make a timer and then you can make it stop when calling a stopCountdown function, right?
For a timer, it is simply asking javascript to - 1 seconds for every 1000 ms passed. So we can write a function which -1 seconds and ask JS to run it every 1000ms, right?
In this case, you should use setInterval but not setTimeout (setTimeout can also make a timer, I will also show you). The difference is that setTimeout calls a function ONCE after X milliseconds and setInterval will run a function EVERY X milliseconds.
Here is the code
let countdownIntervalId // get the countdownIntervalId outside by first declearing a variable to catch the id
function countdown(start) { // assume the unit of start is minute
console.log("countdown called, minutes =" + start)
// add code here to change the innerHTML of the timer if you want
let secondsToCount = start * 60; //Converting minutes to seconds
countdownIntervalId = setInterval(() => {
timer()
}, 1000); // starting to count down
function timer() { // run every seconds
const minutes = Math.floor(secondsToCount / 60);
let seconds = secondsToCount - minutes*60;
console.log("counter= " + minutes + ':' + `${seconds}`.padStart(2, '0'))
secondsToCount = secondsToCount-1;
if (minutes <= 0 && seconds <= 0) {
clearInterval(countdownIntervalId); // clearInterval
console.log('timerOver')
}
}
}
function stopCountdownOutside(){
if(countdownIntervalId){
clearInterval(countdownIntervalId)
}
}
countdown(2) //countdown 2 mins
You can stop the counter by calling stopCountdownOutside(), you can test on Chrome console. This is because we are passing the intervalId to the countdownIntervalId which is declare outside the function. so we can simply call clearInterval(countdownIntervalId) to stop it
For using the setTimeout
let countdownTimeoutId// get the countdownIntervalId outside by first declearing a variable to catch the id
function countdown(start) { // assume the unit of start is minute
console.log("countdown called, minutes =" + start)
// add code here to change the innerHTML of the timer if you want
let secondsToCount = start * 60; //Converting minutes to seconds
countdownTimeoutId = setTimeout(() => {
timer()
}, 1000); // starting to count down
function timer() { // run every seconds
const minutes = Math.floor(secondsToCount / 60);
let seconds = secondsToCount - minutes*60;
console.log("counter= " + minutes + ':' + `${seconds}`.padStart(2, '0'))
secondsToCount = secondsToCount-1;
if (minutes <= 0 && seconds <= 0) {
clearTimeout(countdownTimeoutId); // clearTimeout
console.log('timerOver')
}else{
countdownTimeoutId = setTimeout(timer,1000)
}
}
}
function stopCountdownOutside(){
if(countdownTimeoutId){
clearTimeout(countdownTimeoutId)
}
}
countdown(1) //countdown 2 mins
you can try to refactor my code to a more clean version, happy coding
I have the following code and cannot get the chat timestamp to update without refresh. I am new to javascript.
function timeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = seconds / 31536000;
if (interval > 1) {
return Math.floor(interval) + " years";
}
interval = seconds / 2592000;
if (interval > 1) {
return Math.floor(interval) + " months";
}
interval = seconds / 86400;
if (interval > 1) {
return Math.floor(interval) + " days";
}
interval = seconds / 3600;
if (interval > 1) {
return Math.floor(interval) + " hours";
}
interval = seconds / 60;
if (interval > 1) {
return Math.floor(interval) + " minutes";
}
return Math.floor(seconds) + " seconds";
}
setInterval(timeSince, 1000);
var aDay = 24*60*60*1000;
console.log(timeSince(new Date(Date.now()-aDay)));
console.log(timeSince(new Date(Date.now()-aDay*2)));
const dates = new Date(message.timestamp)
if (message.user === currentUser) position = 'right';
const messageItem = `
<div class="message ${position}">
<span class="small">${timeSince(dates)}</span><br>
I tried: setInterval(timeSince, 1000); and setInterval(timeSince(), 1000); and setInterval(timeSince(date, 1000); and tried each in different place within the function, can't get to work.
timeSince just returns your formatted string with the duration. Setting an interval on this function will do practically nothing. You have to use the return value.
setInterval(function(){
var time = timeSince(message.timestamp);
//You can use a better selector here,
//but without more information, this is the best I can do.
document.getElementsByClassName("small")[0].innerHTML = time;
}, 1000);
found a half solution. I had to insert the setInterval under the function that calls the function under which sits the timesince function. Now the whole thing updates after the interval i set and not just the time, but setting an update interval of 1 minute makes it fine with me.
Apparently since I have several functions under one, the setInterval doesn't work, or didn't work in my case.
draw message is the function under which the timeSince function resides
Maybe coz i didn't provide this info here before i didn't get the right help
Solution:
this:
function getConversation(recipient) {
setInterval(function(){
$.getJSON(`/api/v1/message/?target=${recipient}`, function (data) {
messageList.children('.message').remove();
for (let i = data['results'].length - 1; i >= 0; i--) {
drawMessage(data['results'][i]);
}
messageList.animate({scrollTop: messageList.prop('scrollHeight')});
});
}, 60000);}
or this:
function getConversation(recipient) {
$.getJSON(`/api/v1/message/?target=${recipient}`, function (data) {
messageList.children('.message').remove();
for (let i = data['results'].length - 1; i >= 0; i--) {
drawMessage(data['results'][i]);
}
**setInterval(getConversation(recipient), 1000);**
messageList.animate({scrollTop: messageList.prop('scrollHeight')});
});
}
Thank you David for your help
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);
};
}
Related to this previous Question: How to stop a Javascript Timer after the second countdown?
I figured out a way for my Pomodoro clock to function properly, but my code is pretty repetitive. I couldn't figure out a way to not recreate the same (or very similar function) without the second break timer continuously running. Any ideas?
function startTimer(duration, display) {
var timer = duration, minutes, seconds;
var countdown = setInterval(function () {
minutes = parseInt(timer / 60, 10)
seconds = parseInt(timer % 60, 10);
//minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.textContent = minutes + ":" + seconds;
$(".startClock").click(function () {
clearInterval(countdown);
});
if (--timer < 0) {
clearInterval(countdown);
breakClock();
}
}, 500);
}
function breakTimer(duration, display) {
var timer = duration, minutes, seconds;
var countdown = setInterval(function () {
minutes = parseInt(timer / 60, 10)
seconds = parseInt(timer % 60, 10);
//minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.textContent = minutes + ":" + seconds;
$(".startClock").click(function () {
clearInterval(countdown);
});
if (--timer < 0) {
clearInterval(countdown);
}
}, 500);
$("body").css("background-color", "#E13F86");
$("#title").text(function(i, text){
return text === 'Breaktime' ? 'Productive Countdown' : 'Breaktime'
});
}
var display = document.querySelector('#time');
function startClock() {
var twentyfiveMinutes = 60 * .25;
startTimer(twentyfiveMinutes, display);
}
function breakClock() {
var fiveMinutes = 60 * .05;
breakTimer(fiveMinutes, display);
}
$(".startClock").on("click", function() {
startClock();
});
Here's my codepen as well: http://codepen.io/cenjennifer/pen/pjzBVy?editors=101
Your code is fine. jfriend00 is right, this code does belong on the code review page.
But since I'm here, I will give some feedback. Make an object (using function(){}), and put all of your functions inside of the object.
Instead of recreating all your variables in each function, make them properties inside of the object, so that they are accessible to all of the functions, and so that you don't need to keep recreating them. (Variables like timer, hour, minutes, should be moved as object properties).
Also, don't use global variables, they can interfere with libraries you may find to be useful later on. You should namespace, so that the code is more organized, and does not get overwritten by other globals.
The easy way to reuse the function, is to share the timer returned by setInterval and provide a callback in the startTimer function (used for both start and break). The callback, if provided, takes care of the code to run after the timer finishes. Therefore the specific code can be omitted from startTimer. Also since the timer is shared, no matter when started, the original loop can be stopped when called again (the button is clicked again), so that too can be omitted from the startTimer function, thus making it ready for reusability. The whole would look something like:
var currentcountdown;
var display = document.querySelector('#time');
function startTimer(duration, callback) {
var timer = parseInt(duration), minutes, seconds;
clearInterval(currentcountdown);
currentcountdown = setInterval(function () {
minutes = Math.floor(timer / 60);
seconds = Math.floor(timer % 60);
//minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.textContent = minutes + ":" + seconds;
if (--timer < 0) {
clearInterval(currentcountdown);
if(callback)
callback();
}
}, 500);
}
function startClock() {
$("body").css("background-color", "black");
$("#title").text('Productive Countdown');
var twentyfiveMinutes = 60 * .25;
startTimer(twentyfiveMinutes, breakClock);
}
function breakClock() {
$("body").css("background-color", "#E13F86");
$("#title").text('Breaktime');
var fiveMinutes = 60 * .05;
startTimer(fiveMinutes);
}
$(".startClock").on("click", function() {
startClock();
});
As mentioned in the other post, synthetic sugar would be to put all values into objects (such as duration/background/title) and optionally put the timer and its variables (display/timer) into an object, but for straight forward re-usability of the function the above should provide a good start.