I'm working on a small project between school projects, and I'm having a problem with a DOM element where I use innerText to go from a clock to text, and then after 5s, I want to empty that text. Everything works but the text flicker after a while.
// Break Time // Break Time // Break Time // Break Time
let inputTime = document.getElementById('breakTimeInput');
document.querySelector('#cta-paus').addEventListener('click', function () {
if (inputTime.value === '') {
swal('Break Time', 'You forgot to set break time!', 'warning');
return;
}
let breakTime = inputTime.value * 60;
setInterval(updateCountdown, 1000);
function updateCountdown() {
let minutes = Math.floor(breakTime / 60);
let seconds = breakTime % 60;
// Adds zero infront of secunds
seconds = seconds < 10 ? '0' + seconds : seconds;
// Out puts the time
document.querySelector(
'#MyClockDisplayDown'
).innerText = `${minutes}:${seconds}`;
breakTime--;
// Removes zero when minutes are done
if (minutes == 0) {
document.querySelector('#MyClockDisplayDown').innerText = `${seconds}`;
}
// If the count down is finished, write some text
if (breakTime < 0) {
document.querySelector('#MyClockDisplayDown').innerText = 'On Air';
inputTime.value = '';
setTimeout(() => {
document.querySelector('#MyClockDisplayDown').innerText = '';
}, 2000);
}
}
});
You would need to call clearInterval when finished: the updateCountdown function is still called every second
Something like this?
...
document.querySelector('#cta-paus').addEventListener('click', function () {
...
var intervalId = setInterval(updateCountdown, 1000);
function updateCountdown() {
...
// If the count down is finished, write some text
if (breakTime < 0) {
...
clearInterval(intervalId); // <=== Add this
setTimeout(() => {
document.querySelector('#MyClockDisplayDown').innerText = '';
}, 2000);
}
}
Related
I want to display an animated number from 0 to max value y during x seconds. I have tried this following code but it take too much to complete and clear the interval.
jQuery('.numbers').each(function(item, index) {
const $obj = jQuery(this);
let objValue = parseInt($obj.text()),
currentValue = 0,
speed = 1,
time = 4000,
step = Math.floor(objValue / time);
$obj.text(currentValue);
let interVal = setInterval(() => {
if (currentValue >= objValue) {
clearInterval(interVal);
$obj.text(objValue);
}
$obj.text(currentValue);
currentValue += step
}, speed);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span class='numbers'>7586</span>
<span class='numbers'>147520</span>
How do I play this animation during exactly time seconds?
It is better not to depend on the timing of setInterval(), but the real problem in your script is that you use the floored value to decide the new value to print out.
It is better to use Window.requestAnimationFrame() and create a update() function that prints the current number based on the real time elapsed.
let start, previousTimeStamp;
let numbers = document.querySelectorAll('.numbers');
requestAnimationFrame(update);
function update(timestamp) {
if (start === undefined) {
start = timestamp;
}
const elapsed = timestamp - start;
[...numbers].forEach(elm => {
if(!elm.dataset.start){
elm.dataset.start = elm.textContent;
}
let start = parseInt(elm.dataset.start);
elm.textContent = Math.floor(start / 4000 * elapsed);
});
if (elapsed < 4000) {
previousTimeStamp = timestamp;
requestAnimationFrame(update);
}else {
start = undefined;
[...numbers].forEach(elm => {
elm.textContent = elm.dataset.start;
});
}
}
<span class='numbers'>7586</span>
<span class='numbers'>147520</span>
I'm trying to create a 'Pomodoro' timer that takes user input and creates a timer based on how long somebody wants to use it for (for no reason other than to learn and use it for myself).
I'm finding that my for loops aren't behaving as I'd expect them to and when you look at the timer itself, it is counting down every second, however the timer itself actually reduces by 6 seconds for every one second counted.
I also can't seem to get the timer to move on to the next bit once it hits zero.
I did originally have breaks in the function so that it would move from the current time to the rest time but that didn't seem to do the trick.
In terms of the 6 seconds problem, I'm not even sure where to begin with that.
// set up a counter for how many times you want to set the pomodoro for - users will input how many cycles they want the program to go through.
const pomodoroQuestion = prompt("How many times would you like to use the pomodoro (1 Pomodoro = 3x 25 minute working burst, 2x 5 minute breaks and 1x 15 minute break)");
const pomodoroLength = parseInt(pomodoroQuestion);
for (let i = 0; i < pomodoroLength; i++) {
function startTimer() {
const currentTime = document.getElementById('pomodoroClock').innerHTML;
const timeArray = currentTime.split(/[:]+/);
let minutes = timeArray[0];
let seconds = secondsTimer((timeArray[1] - 1));
if (seconds === 59) {
minutes = minutes - 1;
}
if (minutes < 0) {
alert("Time's up");
}
document.getElementById('pomodoroClock').innerHTML = `${minutes}:${seconds}`;
setTimeout(startTimer, 1000); // Make the function countdown each second
}
// cycle through the seconds
function secondsTimer(sec) {
if (sec < 10 && sec >= 0) {
sec = `${0}${sec}`;
}
if (sec < 0) {
sec = 59;
}
return sec;
}
// the following loop will be what starts the actual pomodoro cycle.
for (let x = 0; x < 3; x++) {
// function starting a countdown timer for 25 minutes
document.getElementById('pomodoroClock').innerHTML = `${25}:${00}`;
startTimer();
if (x < 2) {
// this is where you're going to perform the function that'll allow for a 5 minute break
document.getElementById('pomodoroClock').innerHTML = `${05}:${00}`;
startTimer();
} else {
// this is where you're going to perform the function that'll allow for a 15 minute break
document.getElementById('pomodoroClock').innerHTML = `${15}:${00}`;
startTimer();
}
}
} // end pomodoroLength loop
<div id="pomodoroClock" class="timer"></div>
<script src="script/script.js"></script>
Where am I going wrong with this one? I feel like I'm just missing a few key pieces of understanding with projects like this, hence creating little practice projects to improve.
I think it's worthwhile to change your approach. What if you had a stand-alone countdown() function that displays minutes and seconds in a given target element, and notifies you when it's done?
That's easy to do with promises. You make a function that returns a new Promise, and you resolve() that promise when the time hits zero:
function countdown(minutes, seconds, targetElement) {
return new Promise(resolve => {
const tick = setInterval(function () {
// count down, display current time in targetElement
if (/* time's up */) {
// stop interval, call resolve()
}
}, 1000);
});
}
And since this function returns a promise, it becomes straightforward to chain multiple of those functions with async/await:
async function countdownSequence(...timers) {
for (let t of timers) {
await countdown(0, t, document.getElementById('target'));
}
alert('done!');
}
countdownSequence(5, 10, 5); // counts 5, 10, and 5 seconds, and then alerts 'done!'
Full implementation with a few extras. Note that for the sake of the example, instead of using your sequence 25, 5, 25, 5, 25, 15 for each round, I'm using 5, 2, 5, 2, 5, 3, and I'm using the seconds slot of the countdown function.
function countdown(minutes, seconds, targetElement) {
const pad = num => (num < 10 ? '0' : '') + num;
const display = () => targetElement.textContent = pad(minutes) + ':' + pad(seconds);
return new Promise(resolve => {
const tick = setInterval(function () {
seconds--;
if (seconds < 0) {
minutes--;
seconds = 59;
}
if (minutes < 0) {
clearInterval(tick);
resolve();
}
display();
}, 1000);
display();
});
}
async function pomodoro(numCycles, targetElement) {
targetElement.classList.add('active');
for (let i = 0; i < numCycles; i++) {
targetElement.classList.remove('work');
for (let minutes of [5, 2, 5, 2, 5, 3]) {
targetElement.classList.toggle('work');
await countdown(0, minutes, targetElement);
}
}
targetElement.classList.remove('active');
}
async function start() {
const cycles = parseInt(prompt("How many times would you like to use the pomodoro (1 Pomodoro = 3x 25 minute working burst, 2x 5 minute breaks and 1x 15 minute break)"), 10);
if (cycles > 0) {
await pomodoro(cycles, document.getElementById('pomodoroClock'));
alert("Finished!");
}
}
start();
#pomodoroClock {
display: none;
}
#pomodoroClock.active {
display: block;
color: blue;
}
#pomodoroClock.work {
color: green;
}
#pomodoroClock::after {
padding-left: 5px;
content: '(pause)';
}
#pomodoroClock.work::after {
padding-left: 5px;
content: '(work time)';
}
<div id="pomodoroClock"></div>
Im wondering what is wrong with this for loop here. I'm trying to make a Pomodoro Study Timer, a study technique that suggests that you break down studying into 25-minute chunks that are followed by 3-5 minute breaks. here I have 2 timers that run in sequence, one after the other. When the first timer reaches zero, the second one starts. For now, i have timers set to 5 seconds and 3 seconds respectively in order to make testing quicker. It all works fine until I put the whole thing into a for loop which then brings some unexpected behaviour. I want to loop the entire function based on user input which informs the code on how many times to loop the counters(this isnt setup yet).
The timers are started by pressing a button on an html page. The button executes the pomo() function at the bottom, which contains a loop that should loop the start() function.
PS, I'm a total ultra noob so apologies if this is just terrible code, I'm really new to this :)
var time25 = 5;
var time5 = 3;
var timeElapsed25 = 0;
var timeElapsed5 = 0; // initializes time elapsed to zero
var time = document.getElementsByClassName("header"); //links to html
time[0].innerHTML = time25; // sets output to html
function convertToMin(s) {
mins = Math.floor(s / 60);
let minsStr = mins.toString();
if (minsStr.length === 1) {
mins = '0' + mins;
}
sec = s % 60;
let secStr = sec.toString();
if (secStr.length === 1) {
sec = '0' + sec;
}
return mins + ':' + sec;
}
function start() {
var timer25 = setInterval(counter25, 1000);
console.log("timer1");
function counter25() {
timeElapsed25++
time[0].innerHTML = convertToMin(time25 - timeElapsed25);
if (timeElapsed25 === time25) {
console.log("timer2")
clearInterval(timer25);
timeElapsed25 = 0;
var timer5 = setInterval(counter5, 1000);
function counter5() { //Counter For 5 minute break
timeElapsed5++;
time[0].innerHTML = convertToMin(time5 - timeElapsed5);
if (timeElapsed5 === time5) {
clearInterval(timer5);
timeElapsed5 = 0;
}
}
}
}
}
function pomo() {
for (j = 0; j < 3; j++) {
start();
}
}
You shouldn't call start() in a loop. setInterval() doesn't wait for the the countdown to complete, it returns immediately, so you're starting all 3 timers at the same time.
What you should do is call start() again when both timers complete. To put a limit on the number of repetitions, use a count parameter, and decrement it each time you call again.
var time25 = 5;
var time5 = 3;
var timeElapsed25 = 0;
var timeElapsed5 = 0; // initializes time elapsed to zero
var time = document.getElementsByClassName("header"); //links to html
time[0].innerHTML = time25; // sets output to html
function pomo() {
start(3);
}
function start(count) {
if (count == 0) { // reached the limit
return;
}
var timer25 = setInterval(counter25, 1000);
console.log("timer1");
function counter25() {
timeElapsed25++
time[0].innerHTML = convertToMin(time25 - timeElapsed25);
if (timeElapsed25 === time25) {
console.log("timer2")
clearInterval(timer25);
timeElapsed25 = 0;
var timer5 = setInterval(counter5, 1000);
function counter5() { //Counter For 5 minute break
timeElapsed5++;
time[0].innerHTML = convertToMin(time5 - timeElapsed5);
if (timeElapsed5 === time5) {
clearInterval(timer5);
timeElapsed5 = 0;
start(count - 1); // Start the next full iteration
}
}
}
}
}
function convertToMin(s) {
mins = Math.floor(s / 60);
let minsStr = mins.toString();
if (minsStr.length === 1) {
mins = '0' + mins;
}
sec = s % 60;
let secStr = sec.toString();
if (secStr.length === 1) {
sec = '0' + sec;
}
return mins + ':' + sec;
}
I was trying to implement a function, which is supposed to post measurement A every 5 sec for 10 times, and then post measurement B every 5 sec for a random amounts of time. And I want repeat this function forever as I was trying to implement a fake agent.
So I had the code:
let intervalId = null, repeat = 0;
while (true) {
intervalId = setInterval(() => {
if (repeat < 5) {
// post measurement A
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
repeat = 0;
intervalId = setInterval(() => {
if (repeat < Math.floor(Math.random() * 11)) {
// post measurement B
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
}
The two setInterval() function didn't happen consecutively as I expected, instead they happened at the same time. And the while (true) loop seems not behave as expected either. I'm just wondering is there any way to get around with this problem? Thanks.
You can create two function, one is doA() and one is doB().
Start with doA(), count the number of time //do A is called, when it reached 10, clearInterval and call doB().
In doB(), set the min and max time it should be called, then when it reached randTime clearInterval and doA()
function doA() {
let count = 0;
const a = setInterval(() => {
//do A
console.log('do A');
count += 1;
if (count === 10) {
clearInterval(a);
doB();
}
}, 5000/10);
}
function doB() {
// set your min and max for B
const minTime = 1;
const maxTime = 10;
const randTime = Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime;
let count = 0;
const b = setInterval(() => {
// do B
console.log(randTime);
console.log('do B');
count += 1;
if (count === randTime) {
clearInterval(b);
doA();
}
}, 5000 / randTime);
}
doA();
Working on top of your code, first thing first, remove infinite while loop. It will run endlessly in synchronous fashion while setInterval is asynchronous. repeat value will be far ahead before you do repeat += 1.
Second, break them down in function so they have their own closure for intervalId and repeat value.
function intervalA () {
let intervalId = null
let repeat = 0
intervalId = setInterval(() => {
if (repeat < 5) {
console.log(new Date(), 'A')
// post measurement A
repeat += 1; // increment repeat in callback.
}
else {
clearInterval(intervalId); // done with interval, clear the interval
intervalB(); // and start interval B
}
}, 1000)
}
function intervalB () {
let repeat = 0
let randomEnd = Math.floor(Math.random() * 11) // calculate when it should end.
let intervalId = setInterval(() => {
if (repeat < randomEnd) {
console.log(new Date(), 'B will finish in', randomEnd, 'times')
repeat += 1
}
else {
clearInterval(intervalId) // clear the interval once done
}
}, 1000)
}
intervalA(); //start with interval A
Currently, the intervals are being set at once, synchronously, at the start of your script and during every while thereafter. It would probably be clearer if you only a single interval, with a variable that indicated which measurement to run, and change that variable every random-nth iteration:
const getRandomRepeats = () => Math.ceil(Math.random() * 11)
let repeatsRemaining = getRandomRepeats();;
let measureA = true;
setInterval(() => {
repeatsRemaining--;
if (repeatsRemaining === 0) {
repeatsRemaining = getRandomRepeats();
measureA = !measureA;
}
console.log('repeats remaining: ' + repeatsRemaining);
if (measureA) {
console.log('posting a');
} else {
console.log('posting b');
}
}, 1000);
I'm trying to make a jQuery countdown type animation, that once it hits 0 it executes a function. However I'm having problems because I'm unsure how to go about doing this. I thought I'd do a while loop then pause for a second until it hits 0. However it doesn't seem possible to pause a while loop. So I'm wondering what's the best way to do this? Thanks.
countdown takes an HTMLElement to display itself and the number of seconds to count down for
It returns a Promise that resolves when the counter reaches 0
We can use a .then call to apply a function when the count-down has completed
function countdown(elem, s) {
return new Promise(function(resolve) {
function loop(s) {
elem.innerHTML = s
if (s === 0)
resolve(elem)
else
setTimeout(loop, 1000, s - 1)
}
loop(s)
})
}
countdown(document.querySelector('#a'), 3).then(
function(elem) { console.log('done', elem) }
)
countdown(document.querySelector('#b'), 5).then(
function(elem) { console.log('done', elem) }
)
countdown(document.querySelector('#c'), 10).then(
function(elem) { console.log('done', elem) }
)
<p id="a"></p>
<p id="b"></p>
<p id="c"></p>
You should also be aware that setTimeout and setInterval do not guarantee that the milliseconds argument used is 100% accurate …
var last = Date.now()
var interval = setInterval(function() {
var now = Date.now()
var delta = now - last
console.log(delta)
last = now
}, 1000)
setTimeout(clearInterval, 10000, interval)
// 1000
// 1003
// 998
// 1002
// 999
// 1007
// 1001
// ...
If you need a long running timer with high accuracy, I recommend you adapt the solution to use delta-based updates to the clock. If you rely upon setTimeout or setInterval for accuracy, you will be sad.
function countdown(elem, ms) {
return new Promise(function(resolve) {
function loop(ms, last) {
let now = Date.now()
let delta = now - last
if (ms <= 0) {
elem.innerHTML = 0
resolve(elem)
}
else {
elem.innerHTML = (ms/1000).toFixed(3)
setTimeout(loop, 25, ms - delta, now)
}
}
loop(ms, Date.now())
})
}
countdown(document.querySelector('#a'), 3000).then(
function(elem) { console.log('done', elem) }
)
countdown(document.querySelector('#b'), 5000).then(
function(elem) { console.log('done', elem) }
)
countdown(document.querySelector('#c'), 10000).then(
function(elem) { console.log('done', elem) }
)
<p id="a"></p>
<p id="b"></p>
<p id="c"></p>
Code:
var counter = 10;
var yourFunc = function(){}
var interval = setInterval(function(){
counter--;
if(counter <=0){ yourFunc(); clearInterval(interval); }
}, 1000);
I would use a recursive function
var countDown = function(secondsRemaining){
secondsRemaining -= 1;
if(secondsRemaining <= 0){
//execute
} else {
//wait 1 second and call again
setTimeout(function(){
countDown(secondsRemaining);
}, 1000);
}
}
then to initially start countdown (5 seconds)
countDown(5000);
I would use something like the following :
$(document).ready(function(){
var counter=10;
countDown();
function countDown(){
$('#showNumber').text(counter--);
if(counter>=0)
window.setTimeout(countDown,1000)
else
otherFunction();
}
function otherFunction(){
$('#showNumber').text('FINISHED!');
}
});
Try this out. It does not require jQuery.
var count = 5; // Number of times to run 'counter_function'.
// Run 'counter_function' every second (1000ms = 1 second)
var counter = setInterval(function(){
counter_function()
}, 1000);
// The function to run when 'count' hits 0.
var done_function = function() {
console.log('done');
}
// The function to run at each interval.
var counter_function = function() {
console.log('count');
count--;
if(count === 0){
done_function();
clearInterval(counter);
}
}
It will print the word 'count' every second for 5 seconds, and at the last second it will also print 'done'.
Are you looking for something like this OP?
This executes every second. You can use clearInterval() just as I added in the comment section whenever you want it to stop.
var start = 10; // time to countdown from in seconds
var interval = setInterval(
function(){
if (start == 0) {
complete();
clearInterval(interval);
}
$(".update").html("<h4>Countdown "+start+"</h4>");
start--;
}, 1000);
function complete() {
console.log("called the callback, value of start is: "+start);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="update">
</div>