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().
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;
}
https://jsfiddle.net/mhLaj3qy/1/
const changeTimeset = setTimeout(()=>{
if (is_paused) return;
bgImage.style.backgroundImage = array[current]
changeBackgroundImages(data, ++current % array.length)
}, a)
function chandgeBackgroundImageOnMouse() {
samoyed.addEventListener("mouseover", ()=>{
bgImage.style.backgroundImage = house
is_paused = true
})
samoyed.addEventListener("mouseleave", ()=>{
is_paused = false
})
}
chandgeBackgroundImageOnMouse()
}
How to pause setTimeout and then continue it? It should be like when it hovered on text - picture stops auto slider, when it mouseout - auto slider is working
Here i tried to do it with it_paused = false/true. But nothing succeeded. What am i doing wrong?
In this case, you may try using setInterval instead of setTimeout.
About setInterval:
https://www.w3schools.com/jsref/met_win_setinterval.asp
In fact, setTimeout internally can't be paused, but we can work around, it by when pause clearing the old setTimeout and checking the difference in time, and setting it in a variable called remaining, then when we resume creating a new setTimeout with the remaining time.
class Timer {
constructor(callback, delay) {
this.remaining = delay
this.callback = callback
this.timerId = undefined;
this.start = undefined;
this.resume()
}
pause() {
window.clearTimeout(this.timerId);
this.timerId = null;
this.remaining -= Date.now() - this.start;
};
resume() {
if (this.timerId) {
return;
}
this.start = Date.now();
this.timerId = window.setTimeout(this.callback, this.remaining);
};
}
const pause = document.getElementById('pause');
const resume = document.getElementById('resume');
var timer = new Timer(function() {
alert("Done!");
}, 5000);
pause.addEventListener('click', () => {
timer.pause();
})
resume.addEventListener('click', () => {
timer.resume();
})
<button id="pause">Pause</button>
<button id="resume">Resume</button>
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
I am just starting in javascript, I would like to have a toggle button that runs a function until it's pressed again. I have some code for the toggle:
const backgroundButton = document.getElementById("backgroundButton"); //getting button from HTML
let clickToggle = 0; //defining a veriable used to store the state of the button
backgroundButton.addEventListener("click", function(){
if (clickToggle == 0) {
console.log('button was off');
//start loop
clickToggle = 1;
}
else {
console.log('button was on');
//stop loop
clickToggle = 0;
}
});
but, I don't know how to do an infinite loop, any help is greatly appreciated!
(PS: I know infinite loops crash, that's why I'm here)
You could use setInterval and clearInterval. A truly infinite loop would look like this:
while (true) doSomething();
But that is not what anyone wants, as it will block the user interface. Using setTimeout or setInterval you allow the event loop to continue to do its work.
I'll assume you want to repeat calling the function doSomething in this demo:
const backgroundButton = document.getElementById("backgroundButton");
let timer = 0;
const output = document.getElementById("output");
backgroundButton.addEventListener("click", function(){
if (timer == 0) {
console.log('button was off');
//start loop
timer = setInterval(doSomething, 50);
}
else {
console.log('button was on');
//stop loop
clearInterval(timer);
timer = 0;
}
});
function doSomething() {
output.textContent = +output.textContent + 1;
}
<button id="backgroundButton">Start/Stop</button>
<div id="output"></div>
You can do something like this. Hopefully following with resolve you're required scenario.
let isEnabled = false;
let interval;
function clickFunction() {
isEnabled = !isEnabled; // toggle enable flag
if (isEnabled) {
console.log('Set Interval', isEnabled)
interval = setInterval(function () {
console.log('Do some work.');
}, 1000);
} else {
console.log('Clear Interval', isEnabled)
clearInterval(interval);
}
}
<button onclick="clickFunction()">Click me</button>
You can also use Window.requestAnimationFrame() for the infinite loop.
const toggleButton = document.querySelector('#toggle');
let hasBegun = false;
let anim;
const tick = () => {
if (hasBegun) {
document.querySelector('#output').textContent += '+';
};
requestAnimationFrame(tick)
};
toggleButton.addEventListener("click", function() {
if (!hasBegun) {
console.log('Starting');
hasBegun = true;
anim = requestAnimationFrame(tick);
} else {
console.log('Stopping');
hasBegun = false;
cancelAnimationFrame(anim);
anim = null;
}
});
<button id="toggle">Start/Stop</button>
<pre id="output"></pre>
Here is Cardio app on CodePen.
http://codepen.io/Mahmoud-Zakaria/pen/vxWzxW?editors=1010
When I want to stop the cardio by stop btn and clear its interval which its refernce in function scope, It doesn't stop/clear.
(function() {
//States
let i = 5;
let play = true;
//DOM
let cardioSec = document.getElementById('cardio-sec');
let cardioStart = document.getElementById('cardio-start');
let cardioStop = document.getElementById('cardio-stop');
//Render
function render(el) {
el.innerHTML = i
};
//Audio
let audio = new Audio("https://raw.githubusercontent.com/mahmoudZakaria90/myCodePenStuff/master/audio/Bepp-beep.mp3");
//Setup
function setInterVals(times, callback) {
i = times;
let timer = setInterval(function() {
console.log(i) //RENDER
render(cardioSec)
i--;
if (i < 1) {
clearInterval(timer);
audio.play();
callback();
}
}, 1000)
return timer;
}
function start() {
setInterVals(5, cardio);
}
function cardio() {
setInterVals(30, rest);
}
function rest() {
setInterVals(15, cardio);
}
function stopCardio() {
clearInterval(setInterVals())
}
cardioStart.onclick = start
cardioStop.onclick = stopCardio
})();
Done:
http://codepen.io/anon/pen/gmdLMr?editors=1010
The "timer" variable was out of the scope, by declaring it as global, you wont have problems ^^