Reputation: 15
I'm trying to learn while-loops in Javascript. Why won't this while loop run even when the button is clicked and the boolean becomes true? Is it even possible to have while-loops react to event-driven variable changes?
let run = false;
let count = 0;
const button = document.getElementById("startstop");
const counter = document.getElementById("counter");
button.addEventListener('click', function() {
run = !run; //clever way to toggle the boolean
})
while (run == true) {
count++;
counter.innerHTML = count;
}
<button id="startstop">Start/Stop</button>
<p id="counter">0</p>
Upvotes: 1
Views: 191
Reputation: 27055
When you start up the page, the while loop actually ends the first time. When toggling the boolean, it will not automatically rerender the dom for you. So if you want to run the while loop, you'll need to wrap it in a function and call it.
PS Running this code will freeze your browser btw. Try adding in a delay
let run = false;
let count = 0;
button.addEventListener('click', function() {
run = !run; //clever way to toggle the boolean
doRun();
})
const doRun = () => {
while (run) {
count++;
counter.innerHTML = count;
}
}
doRun()
<button id="startstop">Start/Stop</button>
<p id="counter">0</p>
Upvotes: 0
Reputation: 177691
The loop is not executed because run is false.
Here we can move the while to the button but that will not work because the tight loop does not give the DOM time to update.
button.addEventListener('click', function() {
run = !run; //clever way to toggle the boolean
while (run) {
count++;
counter.innerHTML = count;
}
})
I do NOT recommend to use while loops at all to update DOM
This will work but the loop will run even when not updating the counter
let run = false;
let count = 0;
const button = document.getElementById("startstop");
const counter = document.getElementById("counter");
button.addEventListener('click', function() {
run = !run; //clever way to toggle the boolean
})
setInterval(function() {
if (!run) return
count++;
counter.innerHTML = count;
},10)
<button id="startstop">Start/Stop</button>
<p id="counter">0</p>
A more elegant version would be to clearInterval
let run = false;
let count = 0;
let tId;
const button = document.getElementById("startstop");
const counter = document.getElementById("counter");
button.addEventListener('click', function() {
run = !run; //clever way to toggle the boolean
if (run) tId = setInterval(function() {
count++;
counter.innerHTML = count;
}, 10)
else clearInterval(tId)
})
<button id="startstop">Start/Stop</button>
<p id="counter">0</p>
Upvotes: 1
Reputation: 7285
The check while (run == true) {
happens right after the event listener is added, at which points the value of run
is still false
.
Moreover a while loop that never stops is going to completely overwhelm the thread, making it impossible for the user to click the button.
You could use setInterval
, which runs a function every x milliseconds.
That way, you can check the value of run
whenever you want, without blocking the thread (which is what the while (run == true) {
does).
let run = false;
let count = 0;
let intervalID = setInterval(tick, 1000); // 1000ms = 1sec
const button = document.getElementById("startstop");
const counter = document.getElementById("counter");
button.addEventListener('click', function() {
run = !run;
button.textContent = run ? "Stop" : "Start";
});
function tick() {
if (run) {
count++;
counter.innerHTML = count;
}
}
<button id="startstop">Start</button>
<p id="counter">0</p>
The problem with this is that you could be off by close to a whole second each time you restart the timer. To improve the time measuring you could instead use the variable count
to store the actual milliseconds passed while the counter is on. Using another variable to store the timestamp at which the last measurement happened (or when the start button was last clicked) would also greatly improve the accuracy.
let run = false;
let count = 0;
let previousTick = Date.now();
let intervalID = setInterval(tick, 100); // 100ms = 0.1sec
const button = document.getElementById("startstop");
const counter = document.getElementById("counter");
button.addEventListener('click', function() {
run = !run;
button.textContent = run ? "Stop" : "Start";
previousTick = Date.now();
});
function tick() {
if (run) {
count += Date.now() - previousTick;
counter.innerHTML = Math.floor(count / 1000);
previousTick = Date.now();
}
}
<button id="startstop">Start</button>
<p id="counter">0</p>
Upvotes: 0
Reputation: 761
The while is running but when the page loads, so when you click on the button the while loop has already passed. You can see this in action when you place your while loop inside the click listener, however this is not the behaviour you want. Instead use an interval to fix this issue like shown below:
let run = false;
let count = 0;
let interval = null;
const delay = 100;
const button = document.getElementById("startstop");
const counter = document.getElementById("counter");
button.addEventListener('click', function() {
run = !run;
if (run === true) {
interval = setInterval(() => {
count++;
counter.innerHTML = count;
}, delay)
} else {
clearInterval(interval)
}
})
<button id="startstop">Start/Stop</button>
<p id="counter">0</p>
Upvotes: 1