When the start button is clicked once, everything works perfectly fine. However, when the start button is clicked multiple times (by accident for example), the speed of the counter increases and the stop button doesn't seem to work any more!
Why is this happening? And what can I do to prevent the start button (if clicked accidentally) from increasing the speed of the timer when it is already running?
<button id="startBtn" onclick="startTimer()">Start</button>
<button id="stopBtn" onclick="stopTimer()">Stop</button>
<h2 id="timer"></h2>
<script>
let myCounter
function startTimer() {
myCounter = setInterval(counter, 200);
}
function stopTimer() {
clearInterval(myCounter);
}
let i = 0;
function counter() {
document.getElementById("timer").innerHTML = i++;
}
</script>
Welcome to StackOverflow.
Within your question, it's unclear if you want the timer to reset if the user clicks the start button again, however with my answer, I came to the conclusion that you didn't.
Here's a modified version of startTimer() which utilizes a guard clause to check if an interval already exists (and if so, don't start again)
function startTimer() {
// Guard clause! If the counter exists, exit the function!
if(myCounter) {
return
}
myCounter = setInterval(counter, 200);
}
A tiny update of the stop function is also needed to set myCounter to null after the counter is stopped:
function stopTimer() {
clearInterval(myCounter);
// Set the counter to Null, because it is still declared even though it has no value! (try removing this line and see what happens when you hit start again)
myCounter = null;
}
Hope this helped :)
I added a variable that can helps you detect if the counter is already clicked or not, with the condition of that variable, you can have what you want, I edited your code.
<button id="startBtn" onclick="startTimer()">Start</button>
<button id="stopBtn" onclick="stopTimer()">Stop</button>
<h2 id="timer"></h2>
<script>
let myCounter
let clicked = false;
function startTimer() {
if(!clicked){
myCounter = setInterval(counter, 200);
}
clicked = true;
}
function stopTimer() {
if(clicked){
clearInterval(myCounter);
}
clicked = false;
}
let i = 0;
function counter() {
document.getElementById("timer").innerHTML = i++;
}
</script>
You could simply disable the start button once clicked, and re-enable it when the stop button is clicked.
let i = 0;
let myCounter;
let startBtn = document.getElementById('startBtn');
let stopBtn = document.getElementById('stopBtn');
let timer = document.getElementById('timer');
function startTimer() {
startBtn.disabled = true;
stopBtn.disabled = false;
myCounter = setInterval(counter, 200);
}
function stopTimer() {
startBtn.disabled = false;
stopBtn.disabled = true;
clearInterval(myCounter);
}
function counter() {
i++; timer.value = i;
}
startBtn.addEventListener('click', startTimer);
stopBtn.addEventListener('click', stopTimer);
<button id="startBtn">Start</button>
<button id="stopBtn" disabled>Stop</button>
<h2><output id="timer">0</output></h2>
As an added measure, you can even hide the disabled button so only the active one is shown.
button:disabled {
display: none;
}
Related
let timer = document.querySelector("#timer");
var counter = 3;
function myFn() {
counter--
if (counter === -1) {
counter = 3
}
timer.innerText = counter
}
btn.onclick = function() {
text.innerHTML += 'clicked' + '<br>'
}
var myTimer = setInterval(myFn, 1000);
<div id="timer"></div>
<button id="btn">Button</button>
<div id="text"></div>
I'm trying with this small code to read the div#timer every second and check for a click condition in console.log() F12. It gives me different error in every way I try to do it.
let timer = document.querySelector("#timer");
let btn = document.querySelector("#btn");
setInterval(() => {
console.log(timer.textContent)
if (timer.textContent === '0') {
btn.click()
}
}, 1000);
Consider the following jQuery example.
$(function() {
var timer = 0;
var counter = 3;
var timeObj = $("#timer");
var btnObj = $("#btn");
var txtObj = $("#text");
var interval;
function myFn() {
if (--counter >= 0) {
txtObj.append("Clicked<br />");
} else {
clearInterval(interval);
}
}
interval = setInterval(function() {
timeObj.html(++timer);
}, 1000);
btnObj.click(myFn);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="timer">0</div>
<button id="btn">Button</button>
<div id="text"></div>
You will want to use setInterval() and not setTimeout().
The setInterval() method, offered on the Window and Worker interfaces, repeatedly calls a function or executes a code snippet, with a fixed time delay between each call.
This method returns an interval ID which uniquely identifies the interval, so you can remove it later by calling clearInterval().
See more: https://developer.mozilla.org/en-US/docs/Web/API/setInterval
Using the -- and ++ before the variable will also apply the change before it is used.
The decrement operator (--) decrements (subtracts one from) its operand and returns the value before or after the decrement, depending on where the operator is placed.
Adjusting the logic here can also ensure that the button click does allow the user to keep performing actions.
I am doing a very simple timer with two buttons: stop and set.
Here is the code:
<h1>0</h1>
<button onclick = 'set()'>Set</button>
<button onclick = 'stop()'>Stop</button>
var click = false;
let interval
function set(){
interval = setInterval(function(){
document.querySelector('h1').textContent = parseFloat(document.querySelector('h1').textContent)+1
},1000)
}
function stop(){
window.clearInterval(interval)
}
I found that if I keep pressing the set button which will set new interval, the speed of adding 1 to h1 will become faster and faster (which is much faster than 1000 ms).
I know I could gather the two buttons to one button, or make the set button become display: none or use other ways to prevent this situation.
But I just wonder why does this happens.
Could someone explain me a little bit about why this happens?
Thanks for any responds?
That's because you are not clearing the previous interval (simply reassigning it) on your set function, so if you click on set three times, you are running three intervals.
The proper code should be:
function set(){
clearInterval(interval);
interval = setInterval(function(){
document.querySelector('h1').textContent = parseFloat(document.querySelector('h1').textContent)+1
}, 1000)
}
an other way, more user friendly ?
const h1_element = document.querySelector('h1')
, btSet = document.querySelector('#bt-set')
, btStop = document.querySelector('#bt-stop')
;
var interval = 0
, counter = 0
;
btSet.onclick =()=>
{
btSet.disabled = true
btStop.disabled = false
interval = setInterval( ()=> { h1_element.textContent = ++counter }, 1000 )
}
btStop.onclick =()=>
{
clearInterval(interval)
btSet.disabled = false
btStop.disabled = true
}
<h1>0</h1>
<button id="bt-set">Set</button>
<button id="bt-stop" disabled>Stop</button>
an other way ? More RELIABLE , More elegant
Leveraging OOP: where you guarantee a unique interval is running per instance
class IntervalManager {
constructor(fn, delay){ this.fn= fn; this.delay= delay;}
start() {this.stop(); this.id= setInterval(this.fn, this.delay);}
stop() {if (this.id) clearInterval(this.id);}
}
//--- use it now
const counter = new IntervalManager(function(){
let ui = document.querySelector('h1')
ui.textContent = parseFloat(ui.textContent)+1
},1000);
<h1>0</h1>
<button onclick = 'counter.start()'>Set</button>
<button onclick = 'counter.stop()'>Stop</button>
Other examples below show the benefit of using this manager:
class IntervalManager {
constructor(fn, i){ this.fn= fn; this.i= i;}
start() {this.stop(); this.id= setInterval(this.fn, this.i);}
stop() {if (this.id) clearInterval(this.id);}
}
//--- use it now
//-- example 1
const timer = new IntervalManager(() => {
document.querySelector('#timer h4').textContent = new Date()
}, 1000)
//-- example 2
counterIncrem= 0
const counter = new IntervalManager(() => {
counterIncrem++;
document.querySelector('#counter h4').textContent = counterIncrem
}, 1000)
<section id="timer">
<h1>Timer</h1>
<h4>_</h4>
<button onclick = 'timer.start()'>Start</button>
<button onclick = 'timer.stop()'>Stop</button>
</section>
<section id="counter">
<h1>counter</h1>
<h4>_</h4>
<button onclick = 'counter.start()'>Start</button>
<button onclick = 'counter.stop()'>Stop</button>
</section>
I can't for the life of my figure out how to get this to work bug free.
The button in the code below needs to do three things.
Start a countdown when clicked (works)
End the countdown automatically, and reset itself when it reaches 0(works)
Reset itself prematurely if its clicked in the middle of a countdown(works, sort of)
Bug: when clicked repeatedly it starts multiple countdowns, and more or less breaks. It needs to either reset itself or start a countdown if clicked repeatedly. There should never be more than one countdown.
It works fines as long as people press the button, wait a second, and then press it again to stop it.
The bug I'm running into is if someone spam clicks it, it starts multiple countdowns and generally just breaks the button. I've tried a lot of different methods to fix it, and this is the closest I've gotten.
var i = 29;
let running=false;
$("#startButton").click(function () {
if(running==false){
var countdown = setInterval(function () {
$("#startButton").text("Reset Timer");
running=true;
$("#stopWatch").html(i);
i--;
if (i <0)
{
$("#startButton").text("Start Timer");
running=false;
clearInterval(countdown);
i = 29;
$("#stopWatch").html(i);
}
$("#startButton").click(function () {
$("#startButton").text("Start Timer");
running=false;
clearInterval(countdown);
i = 29;
$("#stopWatch").html(i+1);
});
}, 1000);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="stopWatch">30</div>
<button id="startButton">Start Timer</button>
Welcome to Stack Overflow #William!
I'm not sure what this means: Reset itself prematurely if its clicked in the middle of a countdown(works, sort of). But I managed to fix your bug on spamming button click and for item 3, i just do reset the countdown from initial state. See snippets below:
// Get attribute value from div `stopwatch`. This is for resetting from default value.
var initial = $('#stopWatch').attr("value");
// Assigned initial value to var i.
var i = initial;
$("#stopWatch").html(i);
let running = false;
// Created a separate function to call from button click.
function run(timer = true) {
if (timer) {
running = true;
$("#startButton").text("Reset Timer");
$("#stopWatch").html(i);
var countdown = setInterval(function () {
i--;
$("#stopWatch").html(i);
if (i <= 0) {
running = false;
$("#startButton").text("Start Timer");
clearInterval(countdown);
i = initial;
$("#stopWatch").html(i);
}
}, 1000);
} else {
running = false;
clearInterval(countdown);
i = 0;
$("#startButton").text("Start Timer");
}
}
$("#startButton").click(function () {
// Check if its not running and var i is not 0
if(!running && i != 0) {
run();
// Check if its running and var i is not 0 to ensure that if someone spam the button it just reset the countdown.
} else if (running && i != 0) {
// Will return the else{} on function run().
run(false);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="stopWatch" value="30"></div>
<button id="startButton">Start Timer</button>
Added some comments on the snippet. Feel free to ask if you have any questions.
I am doing a very simple timer with two buttons: stop and set.
Here is the code:
<h1>0</h1>
<button onclick = 'set()'>Set</button>
<button onclick = 'stop()'>Stop</button>
var click = false;
let interval
function set(){
interval = setInterval(function(){
document.querySelector('h1').textContent = parseFloat(document.querySelector('h1').textContent)+1
},1000)
}
function stop(){
window.clearInterval(interval)
}
I found that if I keep pressing the set button which will set new interval, the speed of adding 1 to h1 will become faster and faster (which is much faster than 1000 ms).
I know I could gather the two buttons to one button, or make the set button become display: none or use other ways to prevent this situation.
But I just wonder why does this happens.
Could someone explain me a little bit about why this happens?
Thanks for any responds?
That's because you are not clearing the previous interval (simply reassigning it) on your set function, so if you click on set three times, you are running three intervals.
The proper code should be:
function set(){
clearInterval(interval);
interval = setInterval(function(){
document.querySelector('h1').textContent = parseFloat(document.querySelector('h1').textContent)+1
}, 1000)
}
an other way, more user friendly ?
const h1_element = document.querySelector('h1')
, btSet = document.querySelector('#bt-set')
, btStop = document.querySelector('#bt-stop')
;
var interval = 0
, counter = 0
;
btSet.onclick =()=>
{
btSet.disabled = true
btStop.disabled = false
interval = setInterval( ()=> { h1_element.textContent = ++counter }, 1000 )
}
btStop.onclick =()=>
{
clearInterval(interval)
btSet.disabled = false
btStop.disabled = true
}
<h1>0</h1>
<button id="bt-set">Set</button>
<button id="bt-stop" disabled>Stop</button>
an other way ? More RELIABLE , More elegant
Leveraging OOP: where you guarantee a unique interval is running per instance
class IntervalManager {
constructor(fn, delay){ this.fn= fn; this.delay= delay;}
start() {this.stop(); this.id= setInterval(this.fn, this.delay);}
stop() {if (this.id) clearInterval(this.id);}
}
//--- use it now
const counter = new IntervalManager(function(){
let ui = document.querySelector('h1')
ui.textContent = parseFloat(ui.textContent)+1
},1000);
<h1>0</h1>
<button onclick = 'counter.start()'>Set</button>
<button onclick = 'counter.stop()'>Stop</button>
Other examples below show the benefit of using this manager:
class IntervalManager {
constructor(fn, i){ this.fn= fn; this.i= i;}
start() {this.stop(); this.id= setInterval(this.fn, this.i);}
stop() {if (this.id) clearInterval(this.id);}
}
//--- use it now
//-- example 1
const timer = new IntervalManager(() => {
document.querySelector('#timer h4').textContent = new Date()
}, 1000)
//-- example 2
counterIncrem= 0
const counter = new IntervalManager(() => {
counterIncrem++;
document.querySelector('#counter h4').textContent = counterIncrem
}, 1000)
<section id="timer">
<h1>Timer</h1>
<h4>_</h4>
<button onclick = 'timer.start()'>Start</button>
<button onclick = 'timer.stop()'>Stop</button>
</section>
<section id="counter">
<h1>counter</h1>
<h4>_</h4>
<button onclick = 'counter.start()'>Start</button>
<button onclick = 'counter.stop()'>Stop</button>
</section>
I currently have a timer , that counts down from 2 minutes.
what I would like to happen is when the button is clicked, it is hidden until the timer runs out and when the timer runs out it is visible/clickable again. I would also like the timer to be hidden until the button is clicked, to be visible when the button is clicked and then to be hidden once the timer runs out.
here is my code
js
function startTimer() {
userInput = 120;
if(userInput.length == 0){
alert("Please enter a value");
} else {
var numericExpression = /^[0-9]+$/;
function display( notifier, str ) {
document.getElementById(notifier).innerHTML = str;
}
function toMinuteAndSecond( x ) {
return Math.floor(x/60) + ":" + x%60;
}
function setTimer( remain, actions ) {
(function countdown() {
display("countdown", toMinuteAndSecond(remain));
actions[remain] && actions[remain]();
(remain -= 1) >= 0 && setTimeout(countdown, 1000);
})();
}
setTimer(userInput, {
0: function () { alert( "Time Is Up. Please Sumbit Vote."); }
});
}
}
html
<div id="countdown"></div>
<input type="button" onclick="startTimer()" value="Start Timer">
fiddle
http://jsfiddle.net/grahamwalsh/qur9r3d8/
You can hide and unhide the button using JS
JSFiddle
Add an ID to your button
<input id="btn" type="button" onclick="startTimer()" value="Start Timer"/>
JScode
function startTimer() {
//hide button
document.getElementById("btn").style.display = "none";
//un-hide timer
document.getElementById("countdown").style.display = "inline";
userInput = 10;
if (userInput.length == 0) {
alert("Please enter a value");
} else {
var numericExpression = /^[0-9]+$/;
function display(notifier, str) {
document.getElementById(notifier).innerHTML = str;
}
function toMinuteAndSecond(x) {
return Math.floor(x / 60) + ":" + x % 60;
}
function setTimer(remain, actions) {
(function countdown() {
display("countdown", toMinuteAndSecond(remain));
actions[remain] && actions[remain]();
(remain -= 1) >= 0 && setTimeout(countdown, 1000);
})();
}
setTimer(userInput, {
0: function () {
alert("Time Is Up. Please Sumbit Vote.");
//un-hide button
document.getElementById("btn").style.display = "inline";
//hide timer
document.getElementById("countdown").style.display = "none";
}
});
}
}
Here is a fiddle with the solution:
Use the display property:
document.getElementById("button1").style.display="none";
and to show:
document.getElementById("button1").style.display="block";
fiddle
Make sure to add button1 as an id to your button:
<input id="button1" type="button" onclick="startTimer()"
The fiddle shows where you should put this code...
I went ahead and built it from scratch using JQuery as your friend suggested. I think all the answers here using your setTimeout are taking the wrong approach. This is more of a job for setInterval which will provide slightly less performance overhead and much cleaner code.
Working Example: http://codepen.io/Chevex/pen/RNomGG
First, some simple HTML to work with.
<div id="timerDisplay"></div>
<button id="startTimer">Start Timer</button>
Next, a simple timer script.
// Passing a function to $() is the same as $(document).on('ready', function () { ... });
// It waits for the entire page to be loaded before running the function, which is usually what you want.
$(function () {
// Get reference to our HTML elements and store them as variables.
// I prepend them with dollar signs to signify they represent HTML elements.
var $startTimer = $('#startTimer');
var $timerDisplay = $('#timerDisplay');
// The initial time of the timer.
var time = 120;
// Hide the timer display for now, until the button is clicked.
$timerDisplay.hide();
// Set up a click handler on our $startTimer button.
$startTimer.click(function () {
// When the button is clicked, do the following:
// Set the disabled property to true for our button.
// Effectively the same as <button id="startTimer" disabled>Start Timer</button>
$startTimer.prop('disabled', true);
// Fade in our timer display DIV element.
$timerDisplay.fadeIn();
// Set a timeRemaining variable to the value of the initial time.
var timeRemaining = time;
// Declare an interval function that runs every second.
// Also get reference to the intervalId that it returns so we can kill it later.
var intervalId = setInterval(function () {
// Every time the interval runs (every second), do the following:
// Create a formatted countdown timestamp using the timeRemaining.
var timeStamp = Math.floor(timeRemaining/60) + ':' + timeRemaining%60;
// Set the text of our timer display DIV element to our formatted timestamp.
$timerDisplay.text(timeStamp);
// If the timeRemaining is zero, clean up.
if (timeRemaining === 0) {
// Kill the interval function so it doesn't run again.
clearInterval(intervalId);
// Fade out our timer display DIV element.
$timerDisplay.fadeOut();
// Show the alert informing the user the timer is up.
alert('Time is up, please submit a vote :)');
// Re-enable the startTimer button.
$startTimer.prop('disabled', false);
}
// Otherwise subtract one second from the timeRemaining and allow the interval to continue.
else {
timeRemaining--;
}
}, 1000);
});
});