Suppose I define the following function
export const startMoving = () => {
let counter = 0;
var intervalId = setInterval(() => {
// Do something…
counter++;
}, 1000);
return intervalId;
};
Although 'counter' is defined with let in the function, it works, but my question is: How do I get the value of 'counter' after a while?
Rafael
Return not only the interval ID, but also a function that returns the current value of counter.
const startMoving = () => {
let counter = 0;
var intervalId = setInterval(() => {
// Do something…
counter++;
}, 1000);
return [intervalId, () => counter];
};
const [intervalId, getCounter] = startMoving();
document.body.addEventListener('click', () => document.body.textContent = getCounter());
click here
Related
I have a timer, I want to do something when textContent of div element === 0;
JavaScript:
function createTimer() {
const display = document.createElement('div');
display.classList.add('display');
display.id = 'display';
display.textContent = '5';
return display;
};
let intervalID;
function startTimer() {
resetTimer();
intervalID = setInterval(() => {
let displayTimer = document.getElementById('display');
let displayNumber = parseInt(displayTimer.textContent);
if (displayTimer.textContent !== '0') displayTimer.textContent = displayNumber - 1;
}, 1000);
};
function resetTimer() {
clearInterval(intervalID);
};
function someFunc() {
// here is a lot of code stuff and timer is working correctly
const timer = createTimer();
};
This is what i tried:
function someFunc() {
const timer = createTimer();
timer.addEventListener('input', () => {
if (timer.textContent === '0') {
console.log(true);
};
});
};
As far as I understood correctly, by creating input event on timer, I always get timer.textContent when it changes, right? I keep track of all the changes that's happening in this div element.
Nothing happens.
Keep track of your count as a number in JavaScript. This creates clarity in what the count is and how it can be manipulated.
Inside the setInterval callback, check if the count is 0. The interval is already there and will run every second, so it makes sense to check it in that place.
The example below is a modified version of your script with the suggested implementation. Since you're returning the display element from the createTimer function I've decided to reuse it in the startTimer function. That way you don't have to select the element from the DOM, as you already have a reference to the element.
As a bonus an extra argument which can be callback function to do something whenever the timer ends.
let count = 5;
let intervalID;
function createTimer() {
const display = document.createElement('div');
display.classList.add('display');
display.id = 'display';
display.textContent = '5';
return display;
};
function resetTimer() {
clearInterval(intervalID);
};
function startTimer(timer, onFinish) {
resetTimer();
intervalID = setInterval(() => {
if (count !== 0) {
count--;
}
if (count === 0) {
resetTimer();
if (typeof onFinish === 'function') {
onFinish();
}
}
timer.textContent = count;
}, 1000);
};
function someFunc() {
const timer = createTimer();
document.body.append(timer);
startTimer(timer, () => {
console.log('Done');
});
};
someFunc();
The input event fires when the value of an <input>, <select>, or <textarea> element has been changed by user. It does not fire when setting the textContent programmatically.
You can use the MutationObserver API to observe the node change.
const timer = createTimer();
new MutationObserver(() => {
let timer = document.getElementById('display');
if (timer.textContent === '0') {
console.log(true);
};
}).observe(timer, { childList: true });
You could implement the timer with the help of async/await code. It makes the code a lot more cleaner, by having your start and end code in the same function.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
function createTimer(name) {
const element = document.createElement("div");
element.classList.add("timer");
document.body.appendChild(element);
element.textContent = name+": -";
return async function(seconds) {
for (let second = seconds; second >= 0; second--) {
element.textContent = name+": "+second;
if (second > 0) {
await wait(1000);
}
}
};
}
async function someFunc() {
const timer = createTimer("First timer");
console.log("first timer started", new Date());
await timer(10);
console.log("timer ended", new Date());
await wait(2500);
console.log("first timer started again", new Date());
await timer(5);
console.log("first timer ended again", new Date());
}
async function someOtherFunc() {
const timer = createTimer("Second timer");
console.log("second timer started", new Date());
await timer(20);
console.log("second timer ended", new Date());
}
someFunc();
someOtherFunc();
.timer {
text-align: center;
font-size: 2em;
font-family: sans-serif;
}
Inside my functional component I have defined two hooks started:false and sec:0
let interval = null
const Home = () => {
const [sec, setSec] = useState(0)
const [started, setStarted] = useState(false)
So as the name suggests, every second I want to increment this counter.
I have method called setTimer which should increment my sec every second.
function setTimer() {
console.log(started)
if (!started) {
console.log(started)
setStarted(true);
interval = setInterval(() => {
setSec(sec+1)
console.log("ADDED",sec)
}, 1000)
}
}
But it seems that the sec counter never goes above 1. Is there a reason for this?
You should uses a functional state update, so instead of setSec(sec+1) write setSec(prevSec => prevSec + 1)
See the React Hooks API for reference: https://reactjs.org/docs/hooks-reference.html#usestate
let started = false;
let sec = 0;
let setSec = function(seconds) { sec = seconds; }
let setStarted = function() { started = true; }
function setTimer() {
console.log(started)
if (!started) {
setStarted(true);
console.log(started)
interval = setInterval(() => {
setSec(sec+1)
console.log("ADDED",sec)
}, 1000)
}
}
setTimer();
var setStarted = false;
setSec = 0;
function setTimer() {
console.log(setStarted)
if (!setStarted) {
console.log(setStarted)
setStarted = true;
setInterval(() => {
setSec +=1;
console.log("ADDED",setSec);
}, 1000)
}
}
setTimer();
setInterval has closed over the initial value of your state(sec). Every time you are modifiying it, you are doing setSec(0+1), essentially making it 1. This is the problem of stale state.
You can use useRef to have access to the current value always.
import "./styles.css";
import { useState, useRef } from "react";
export default function App() {
const [sec, setSec] = useState(0);
const [started, setStarted] = useState(false);
let interval = null;
let realSec = useRef(0);
function setTimer() {
console.log(started);
if (!started) {
console.log(started);
setStarted(true);
interval = setInterval(() => {
setSec(realSec.current + 1);
realSec.current++;
console.log("ADDED", sec);
}, 1000);
}
}
return (
<>
<p>{sec}</p>
<button onClick={setTimer}>X</button>
</>
);
}
I am working on a little challenge with school. We have learned CSS, HTML, and Javascript. I am trying to create a timer that is always running in the background. Then I need a button that pauses said timer and changes into a resume button that will resume the timer. This is what I have come up with.
document.addEventListener("DOMContentLoaded", () => {
const counterElement = document.getElementById('counter')
let counterValue = 0
const pauseButton = document.getElementById('pause')
const resumeButton = document.getElementById('resume')
const submitButton = document.getElementById(`submit`)
const minusButton = document.getElementById(`minus`)
const plusButton = document.getElementById('plus')
const heartButton = document.getElementById('heart')
intervalId = setInterval(myCallback, 1000);
function myCallback() {
counterValue += 1;
counterElement.innerHTML = counterValue;
}
function resume() {
setInterval(myCallback, 1000);
pauseButton.style.display = '';
resumeButton.style.display = 'none';
}
function pause() {
clearInterval(intervalId);
pauseButton.style.display = 'none';
resumeButton.style.display = '';
}
pauseButton.addEventListener("click", (e) => {
pause()
})
resumeButton.addEventListener("click", (e) => {
resume()
})
It does not function properly. Only the first few clicks work.
function resume() {
intervalId = setInterval(myCallback, 1000);
pauseButton.style.display = '';
resumeButton.style.display = 'none';
}
Try this. The root issue is that you're not assigning your setInterval reference back to your intervalId variable. So when you call clearInterval(intervalId) later, your code is saying, "That's already been cleared..." and not doing anything.
In short, your current resume() function creates a NEW setInterval - it doesn't update the old one. And since there was no reference to the new setInterval, there was no way for your pause function to be able to find it and clear it.
When you call setTimeout in the resume function, you have to reassign the intervalId variable to store the new interval ID. If you don't do that, your pause function will keep cancelling the first interval, which is a no-op.
So do this instead:
document.addEventListener("DOMContentLoaded", () => {
const counterElement = document.getElementById('counter')
let counterValue = 0
const pauseButton = document.getElementById('pause')
const resumeButton = document.getElementById('resume')
//const submitButton = document.getElementById(`submit`)
//const minusButton = document.getElementById(`minus`)
//const plusButton = document.getElementById('plus')
//const heartButton = document.getElementById('heart')
//It's a good idea to declare your variables. Since you want to reassign it, you probably want `let`.
let intervalId = setInterval(myCallback, 1000);
function myCallback() {
counterValue += 1;
counterElement.innerHTML = counterValue;
}
function resume() {
//Assign the new interval ID to `intervalId`
intervalId = setInterval(myCallback, 1000);
pauseButton.style.display = '';
resumeButton.style.display = 'none';
}
function pause() {
clearInterval(intervalId);
pauseButton.style.display = 'none';
resumeButton.style.display = '';
}
pauseButton.addEventListener("click", (e) => {
pause()
})
resumeButton.addEventListener("click", (e) => {
resume()
})
})
<button id="pause" >Pause</button>
<button id="resume" style="display:none;" >Resume</button>
<div id="counter" >0</div>
When you create a new Interval in your resume() function, you need to store its return value again in your intervalId variable for the next time you call pause().
As I understood from MDN, I am supposed to make variable and assign setInterval function to it, so that I could use that variable and call clearInterval for it, but for some reason, my code is now working. It is properly fetching data with buttonStart, but will not stop fetching data with buttonStop.
Thank you for your time.
const buttonStart = document.querySelector('#start')
const buttonStop = document.querySelector('#stop')
const list = document.querySelector('#list')
class Price {
constructor(time, price) {
this.time = time
this.price = price
}
}
const fetchBitcoin = async () => {
try {
const res = await fetch('https://api.cryptonator.com/api/ticker/btc-usd');
const data = await res.json();
const newPrice = new Price(data.timestamp, data.ticker.price)
return newPrice
} catch (e) {
console.log("Something went wrong in downloading price", e)
}
}
const addNewPrice = async () => {
const newLI = document.createElement('LI')
const newElement = await fetchBitcoin()
const newTime = convertTime(newElement.time)
newLI.append(newTime, ' ', newElement.price.slice(0, 8))
list.append(newLI)
}
function convertTime(time) {
let unix_timestamp = time
var date = new Date(unix_timestamp * 1000);
var hours = date.getHours();
var minutes = "0" + date.getMinutes();
var seconds = "0" + date.getSeconds();
var formattedTime = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
return formattedTime
}
let interval = buttonStart.addEventListener('click', () => {
setInterval(addNewPrice, 2000)
})
buttonStop.addEventListener('click', () => clearInterval(interval));
You need to create interval variable and assign the return value of the setInterval method rather than addEventListener because addEventListener does not return anything,
let interval;
buttonStart.addEventListener('click', () => {
interval = setInterval(addNewPrice, 2000)
})
You need to adjust the example below to your use case but this is what you need in general:
var timerEl = document.querySelector('.js-timer');
var startBtn = document.querySelector('.js-start');
var stopBtn = document.querySelector('.js-stop');
var intervalId;
var timer = 0;
startBtn.addEventListener('click', function() {
stopTimer();
console.log('start timer');
intervalId = setInterval(execTimer, 100);
});
stopBtn.addEventListener('click', stopTimer);
function stopTimer() {
timer = 0;
console.log('stop timer');
clearInterval(intervalId);
renderTimer();
}
function execTimer() {
timer++;
renderTimer();
console.log('timer score', timer);
}
function renderTimer() {
timerEl.textContent = timer;
}
<span class="js-timer"></span><br />
<button class="js-start">Start</button>
<button class="js-stop">Stop</button>
I'm setting up a timer with moment, is not the only one I have in this screen but this doesn't work:
const halfBellTimer = () => {
const x = setInterval(() => {
let countTime = moment.duration().add({seconds: meditationTime / 2});
if (countTime <= 0) {
console.log('STOP');
} else {
countTime = countTime.subtract(1, 's');
console.log(countTime.seconds());
}
}, 1000);
};
It sets the time correctly but I get a log of the same value, so it doesn't subtract it.
Any idea?
Thanks!
If you move let countTime = moment.duration().add({seconds: meditationTime / 2});
outside of setInterval function it works fine.
Don't forget to clean up with clearInterval.
Take a look at the example.
const halfBellTimer = () => {
const meditationTime = 10;
let countTime = moment.duration().add({
seconds: meditationTime / 2
});
const x = setInterval(() => {
if (countTime <= 0) {
console.log('STOP');
clearInterval(x);
} else {
countTime = countTime.subtract(1, 's');
console.log(countTime.seconds());
}
}, 1000);
};
halfBellTimer();
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>