clearInterval does not seem to be working - javascript

I'm trying to make a simple javascript counter.
Basically, I'm getting an integer value from a user by input and I want to count descending from that value to 0.
So I coded this:
let inputCounter = document.querySelector("#input-counter");
let startBox = document.querySelector(".start-box");
let startCounter = document.querySelector("#start-counter");
let errorMessage = document.querySelector("#error-message");
let timerCircle = document.querySelector(".c100");
let timeSpan = document.querySelector(".c100 > span");
startCounter.addEventListener('click', function() {
let seconds = inputCounter.value;
if (isNaN(seconds)) {
errorMessage.textContent = "Not an integer value";
errorMessage.classList.add("active");
} else {
errorMessage.classList.remove("active");
timerCircle.style.display = "block";
startBox.style.display = "none";
timeSpan.textContent = seconds;
let timerId = setInterval(() => {
seconds -= 1;
if (seconds < 0) {
clearInterval(timerId);
}
timeSpan.textContent = seconds;
}, 1000);
}
});
<div class="container">
<div class="start-box">
<input type="text" id="input-counter" placeholder="type your value in seconds">
<button id="start-counter">Start</button>
<div id="error-message"></div>
</div>
<div class="c100 p50">
<span></span>
<div class="slice">
<div class="bar"></div>
<div class="fill"></div>
</div>
</div>
</div>
So this works fine and properly counts down from that custom entered number but the only problem here is that it goes to -1, -2, -3 and etc.
So that is why I tried determining timerId to the setInterval function and then checking this condition:
if(seconds < 0){
clearInterval(timerId);
}
However it does not clear the interval and still shows Negative numbers...
So what's going wrong here? How can I properly clear the interval when it comes to 0?

Try this code:
let seconds = 10
let timerId = setInterval(() => {
if (seconds <= 0) {
clearInterval(timerId);
}
console.log(seconds);
seconds -= 1;
}, 1000);

Your clearInterval implementation works, but it is missing a return statement following the clear. So it clears the interval correctly (it won't run the setTimeout callback again) but the current execution of the callback continues, so it goes past the if statement and sets -1 in the label anyway. The return statement, halts the execution of that callback when you reach your base case, and leaves the label set to 0.
You could also re-arrange the code to set the label above the if statement, and change the condition to if (seconds === 0), so then following the clear and return, your seconds variable remains at 0, instead of -1.
let inputCounter = document.querySelector("#input-counter");
let startBox = document.querySelector(".start-box");
let startCounter = document.querySelector("#start-counter");
let errorMessage = document.querySelector("#error-message");
let timerCircle = document.querySelector(".c100");
let timeSpan = document.querySelector(".c100 > span");
startCounter.addEventListener('click', function() {
let seconds = inputCounter.value;
if (isNaN(seconds)) {
errorMessage.textContent = "Not an integer value";
errorMessage.classList.add("active");
} else {
errorMessage.classList.remove("active");
timerCircle.style.display = "block";
startBox.style.display = "none";
timeSpan.textContent = seconds;
let timerId = setInterval(() => {
seconds -= 1;
if (seconds < 0) {
clearInterval(timerId);
return; // THIS IS WHAT WAS MISSING
}
timeSpan.textContent = seconds;
}, 1000);
}
});
<div class="container">
<div class="start-box">
<input type="text" id="input-counter" placeholder="type your value in seconds">
<button id="start-counter">Start</button>
<div id="error-message"></div>
</div>
<div class="c100 p50">
<span></span>
<div class="slice">
<div class="bar"></div>
<div class="fill"></div>
</div>
</div>
</div>

If you stop when < 0, it will check:
2, reduce, its now 1, its < 0? NO, so go again
1, reduce, its now 0, its < 0? NO, so go again
1, reduce, its now -1, its < 0? yes, stop: result -1
You can stop in < 1 or <= 0.
console.log(seconds) // 0
Additionally, working with timeout and numeric variables is not so accurate (depends on instabilities in processing).
I suggest saving the start time, and calculating the difference every second.
If the PC freezes for 3 seconds, when it starts up again, 3 seconds have passed and not just 1.
let inputCounter = document.querySelector("#input-counter");
let startBox = document.querySelector(".start-box");
let startCounter = document.querySelector("#start-counter");
let errorMessage = document.querySelector("#error-message");
let timerCircle = document.querySelector(".c100");
let timeSpan = document.querySelector(".c100 > span");
startCounter.addEventListener('click', function() {
let seconds = inputCounter.value;
if (isNaN(seconds)) {
errorMessage.textContent = "Not an integer value";
errorMessage.classList.add("active");
} else {
errorMessage.classList.remove("active");
timerCircle.style.display = "block";
startBox.style.display = "none";
timeSpan.textContent = seconds;
let timerId = setInterval(() => {
console.log({seconds})
seconds -= 1;
if (seconds < 1) {
clearInterval(timerId);
}
timeSpan.textContent = seconds;
}, 1000);
}
});
<div class="container">
<div class="start-box">
<input type="text" id="input-counter" placeholder="type your value in seconds">
<button id="start-counter">Start</button>
<div id="error-message"></div>
</div>
<div class="c100 p50">
<span></span>
<div class="slice">
<div class="bar"></div>
<div class="fill"></div>
</div>
</div>
</div>

Related

how to choose a number to decrement below zero

I'm learning js. I want to make an alarm clock app. Currently I'm working on setting the time. I created buttons that increment and decrement the hour. I want to decrement below zero to 23. I also want to stop the increment at 23 and then continue with 0. Can anyone help me? If someone tells me the first part of the condition, I'll be able to figure out the other part.
let myHour = document.querySelector(".hours");
let myHourConverted = Number(myHour.innerText);
const hourDecrementBtn = document.querySelector(".hour-decrement");
const hourIncrementBtn = document.querySelector(".hour-increment");
hourDecrementBtn.addEventListener("click", decrementHour);
function decrementHour() {
let hourDecrement = --myHourConverted;
myHour.innerText = hourDecrement;
if (hourDecrement < 0) {
hourDecrement = 23;
}
}
hourIncrementBtn.addEventListener("click", incrementHour);
function incrementHour() {
let hourIncrement = ++myHourConverted;
myHour.innerText = hourIncrement;
if (hourIncrement > 23) {
hourIncrement = 0;
}
}
<div class="timer-hours-container">
<span class="hours">00</span>
<div class="hours-buttons-container">
<button class="hour-decrement timer-button">〈</button>
<button class="hour-increment timer-button">〉</button>
The problem is that when you make the change to decrement or increment from 23 to 0, you change the content of hourIncrement but not of myHourConverted, I leave you a small solution.
const myHour = document.querySelector(".hours");
let myHourConverted = Number(myHour.innerText);
const hourDecrementBtn = document.querySelector(".hour-decrement");
const hourIncrementBtn = document.querySelector(".hour-increment");
function decrementHour() {
if (--myHourConverted < 0) myHourConverted = 23;
myHour.innerText = myHourConverted;
}
function incrementHour() {
if (++myHourConverted > 23) myHourConverted = 0;
myHour.innerText = myHourConverted;
}
hourDecrementBtn.addEventListener("click", decrementHour);
hourIncrementBtn.addEventListener("click", incrementHour);
<div class="timer-hours-container">
<span class="hours">00</span>
<div class="hours-buttons-container">
<button class="hour-decrement timer-button">〈</button>
<button class="hour-increment timer-button">〉</button>
</div>
</div>

Why is 'textarea' returning null even on input?

I'm writing a code on speed typing test, and I would like to start the timer when the user inputs the texteare for the first time. However, the textarea doesn't seem to be logging in my input changes and console is returning an error message saying addEventListener cannot return a property of null.
HTML CODE
<section class="typing">
<div class="timer" id="timer"><h1>60</h1></div>
<div class="container">
<div class="word-display" id="word-display">
<p>Typing Test</p>
</div>
<textarea id="word-input" class="word-input" rows="7" placeholder="Start Typing" name="word-input"></textarea>
</div>
</section>
JAVA CODE
function startTimer() {
startTime = new Date();
setInterval(() => {
document.querySelector('#timer').innerText = getTimerTime()}, 1000);
};
function getTimerTime () {
return (60 - Math.floor((new Date() - startTime) / 1000));
};
document.querySelector('DOMContentLoaded', onload = generateWordSpan, onkeyup = checkWord, function() {
if (document.querySelector('#word-input')) {
document.querySelector('#word-input').addEventListener('onchange', startTimer, {once: true})
};
});
The trick to have an event listener to start an interval is the {once: true} option.
That make the event fire only once.
Then... within the interval, you do what you like.
const maxTime = 60 // seconds
const header = document.querySelector('#timer h1')
const input = document.querySelector("#word-input")
function startTimer() {
const startTime = new Date();
let timeIsUP = 0
const intervalId = setInterval(() => {
timeIsUP = maxTime - Math.floor((new Date() - startTime) / 1000)
header.innerText = (timeIsUP);
if (timeIsUP < 0) {
clearInterval(intervalId)
if (input.value.length > 100) {
header.innerText = "Good work. We'll call you."
}
if (input.value.length > 300) {
header.innerText = "We hire you for monday."
}
if (input.value.length > 800) {
header.innerText = "SuperStar! How many vacation weeks you want?"
}
if (input.value.length > 6000) {
header.innerText = "Cheater!"
}
}
}, 1000);
};
input.addEventListener("keyup", startTimer, {
once: true
})
#word-input {
width: 100%;
}
<section class="typing">
<div class="timer" id="timer">
<h1>60</h1>
</div>
<div class="container">
<div class="word-display" id="word-display">
<p>Typing Test</p>
</div>
<textarea id="word-input" class="word-input" rows="7" placeholder="Start Typing" name="word-input"></textarea>
</div>
</section>

Pause and start JS countdown timer

I am trying to develop a centurion countdown timer for my website. It is a drinking game.
The way the timer works is: It is a 60 second countdown timer. Everytime the timer hits 0 it will +1 a shot counter and restart. It will do this 100 times.
The game is you must do 1 shot, every minute for 100 minutes. I am a beginner at JS and I am learning a lot, but I am seriously struggling to get this to work the way I want it to.
All I need is a "Start" Button, a "Pause" button and a "Reset" button but I can't seem to find a way to make these buttons work without messing the timer up.
Here is the HTML code:
<div class="inner">
<h1 class="heading alt">Centurions Timer</h1>
<p>1 Shot. Every Minute. 100 Minutes.</p>
<p>___________________________________</p>
<div id="timer">
<p id="seconds">60</p>
<p id="shots">0</p>
</div>
<input type="button" value="Start" onclick="timer()">
<input type="button" value="Pause" onclick="clearInterval(myTimer)">
</div>
and here is the JS code:
var seconds = 60;
var shots = 0;
var timer;
var c = 60;
function timer() {
timer = setInterval(myTimer, 1000)
}
function myTimer() {
document.getElementById("seconds").innerHTML = --c;
if (c == 0) {
shots = shots + 1;
c = 60;
}
document.getElementById("shots").innerHTML = shots;
}
If anyone could help me and show me what I am doing wrong and how I can make the code better, please do!
First, consider renaming either your method timer() or variable timer to disambiguate between the two.
Next, setInterval() returns an interval ID, which you store in timer.
clearInterval() takes the interval ID as the parameter, so try passing it the timer variable instead of myTimer (a function)
Little bit clean up needed. For clearInterval, u have to id of timer to clear
var seconds = 60;
var shots = 0;
var timer;
var c = 60;
function start() {
clearInterval(timer)
timer = setInterval(( ) =>{
updateUi()
}, 1000);
}
function pause() {
clearInterval(timer)
}
function updateUi() {
document.getElementById("seconds").innerHTML = --c;
if (c == 0) {
shots = shots + 1;
c = 60;
}
document.getElementById("shots").innerHTML = shots;
}
<div class="inner">
<h1 class="heading alt">Centurions Timer</h1>
<p>1 Shot. Every Minute. 100 Minutes.</p>
<p>___________________________________</p>
<div id="timer">
<p id="seconds">60</p>
<p id="shots">0</p>
</div>
<input type="button" value="Start" onclick="start()">
<input type="button" value="Pause" onclick="pause()">
</div>
You can also use Pub Sub model, to make code looks clean.
var seconds = 60;
var shots = 0;
var c = 60;
function Emitter() {
this.cb;
this.on = cb => {
this.cb = cb;
};
this.emit = () => {
this.cb && this.cb();
};
this.cancel = () => {
this.cb = null;
};
}
const emitter = new Emitter();
const tick = () => {
setInterval(() => {
emitter.emit();
}, 1000);
};
tick()
function start() {
emitter.on(updateUi);
}
function pause() {
emitter.cancel();
}
function updateUi() {
document.getElementById("seconds").innerHTML = --c;
if (c == 0) {
shots = shots + 1;
c = 60;
}
document.getElementById("shots").innerHTML = shots;
}
<div class="inner">
<h1 class="heading alt">Centurions Timer</h1>
<p>1 Shot. Every Minute. 100 Minutes.</p>
<p>___________________________________</p>
<div id="timer">
<p id="seconds">60</p>
<p id="shots">0</p>
</div>
<input type="button" value="Start" onclick="start()">
<input type="button" value="Pause" onclick="pause()">
</div>
<div class="inner">
<h1 class="heading alt">Centurions Timer</h1>
<p>1 Shot. Every Minute. 100 Minutes.</p>
<p>___________________________________</p>
<div id="timer">
<p id="minutes">100</p>
<p id="seconds">60</p>
<p id="shots">0</p>
</div>
<input type="button" value="START" onclick="start()">
<input type="button" value="PAUSE" onclick="pause()">
<input type="button" value="RESET" onclick="reset()">
</div>
Changed the onclick functions to something more meaningful. Added an extra button and some more logic to auto-stop when hitting the 100 minutes.
var seconds = 60,
minutes = 100,
shots = 0;
timer = "";
// this will just stop the timer, if you click start it will resume from where it left off
function pause() {
clearInterval(timer);
}
// resets seconds/minutes/shots, stops game
function reset() {
clearInterval(timer);
seconds = 60;
minutes = 100;
shots = 0;
timer = "";
document.getElementById("minutes").innerHTML = minutes;
document.getElementById("seconds").innerHTML = c;
document.getElementById("shots").innerHTML = shots;
}
// starts game from wherever it was left
function start() {
timer = setInterval(function() {
document.getElementById("seconds").innerHTML = c--;
if (c === 0) {
document.getElementById("minutes").innerHTML = --minutes;
// when 100 minutes are up, game will stop and reset
if (minutes === 0) {
reset();
}
shots++;
c = 60;
}
document.getElementById("shots").innerHTML = shots;
}, 1000)
}

why progressbar works separately from countdown

I want to create progress bar countdown.
But the problem is that decimals don't change like 10 9 8 7 .. simultaniously.
this is html :
<progress id="prg" value ="0" max="10"></progress>
<p id="counting">10</p>
This is my js script :
var reverse_count = 10;
var downloadTimer = setInterval(function(){
document.getElementById('prg').value = 10 - --reverse_count;
if(reverse_count <= 0) {
clearInterval (downloadTimer);
document.getElementById('counting').innerHTML = reverse_count;
}
}, 1000);
You need to take document.getElementById('counting').innerHTML = reverse_count; out of the if statement, like so:
var reverse_count = 10;
var downloadTimer = setInterval(function() {
document.getElementById('prg').value = 10 - --reverse_count;
if (reverse_count <= 0) {
clearInterval(downloadTimer);
}
document.getElementById('counting').innerHTML = reverse_count;
}, 1000);
<progress id="prg" value="0" max="10"></progress>
<p id="counting">10</p>
You just added the right statement in the wrong place. The update of the countdown label should also be done each time the interval callback is executed, as you do with the update of the <progress> value, not only once just before calling clearInterval:
const progressBar = document.getElementById('progressBar');
const countdownLabel = document.getElementById('countdownLabel');
let countdown = 10;
const downloadTimer = setInterval(() => {
// This is executed multiple times until the interval is cleared:
countdownLabel.innerHTML = --countdown;
progressBar.value = 10 - countdown;
if (countdown <= 0) {
// This is only executed once when the countdown gets to 0:
clearInterval(downloadTimer);
}
}, 1000);
<progress id="progressBar" value ="0" max="10"></progress>
<p id="countdownLabel">10</p>
In your code the countdown updates iff reverse_count <= 0.
var reverse_count = 10;
var downloadTimer = setInterval(function(){
document.getElementById('prg').value = 10 - --reverse_count;
document.getElementById('counting').innerHTML = reverse_count;
if(reverse_count <= 0) {
clearInterval (downloadTimer);
}
}, 1000);
<progress id="prg" value ="0" max="10"></progress>
<p id="counting">10</p>

Want to create a program that gives a set time (stopwatch) and generates keys within that time all using the same button

I am working on a project that generates a 6 digit string key all the while being timed. So my stopwatch has 3 buttons. Start, stop, and reset. I want to create a program in which, when i click on start it will generate a key and the stopwatch will run. So i want to know how i can have the button execute two actions at once. Same with stop, and reset will generate a new key. Also how can i put these two codes together into one?
My code for key generator:
function Keygenerate() {
var text ="";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for(var i=0; i < 5; i++ ){
text += possible.charAt(Math.floor(Math.random() * possible.length()));
}
return text;
}
My code for the Stopwatch:
<div class="class">
<p>CSC 131 Attendance Tracker</p>
</div>
<div class="stopwatch">
<div class="title">
<h2>KEY:</h2>
</div>
<div class="key">
<input type="text" name="output">
</div>
<div class="time">
<h2>TIME:</h2>
</div>
<div class="display">
<span class="minutes">00:</span><span class="seconds">00:</span><span class="centiseconds">00</span>
</div>
<div class="controls">
<button class="start">Start</button>
<button class="stop">Stop</button>
<button class="reset">Reset</button>
</div>
</div>
<script>
var ss = document.getElementsByClassName('stopwatch');
[].forEach.call(ss, function (s) {
var currentTimer = 0,
interval = 0,
lastUpdateTime = new Date().getTime(),
start = s.querySelector('button.start'),
stop = s.querySelector('button.stop'),
reset = s.querySelector('button.reset'),
mins = s.querySelector('span.minutes'),
secs = s.querySelector('span.seconds'),
cents = s.querySelector('span.centiseconds');
start.addEventListener('click', startTimer);
stop.addEventListener('click', stopTimer);
reset.addEventListener('click', resetTimer);
function pad (n) {
return ('00' + n).substr(-2);
}
function update () {
var now = new Date().getTime(),
dt = now - lastUpdateTime;
currentTimer += dt;
var time = new Date(currentTimer);
mins.innerHTML = pad(time.getMinutes()) + ":";
secs.innerHTML = pad(time.getSeconds()) + ":";
cents.innerHTML = pad(Math.floor(time.getMilliseconds() / 10));
lastUpdateTime = now;
if(now == time.getMinutes()){
clearInterval(interval);
}
}
function startTimer () {
if (!interval) {
lastUpdateTime = new Date().getTime();
interval = setInterval(update, 1);
}
}
function stopTimer () {
clearInterval(interval);
interval = 0;
}
function resetTimer () {
stopTimer();
currentTimer = 0;
mins.innerHTML = secs.innerHTML = cents.innerHTML = pad(0);
}
});
</script>
</body>
Well, at its basic you would do it by attaching two functions to an event:
<button onclick = "generate_key(); stopwatch_run();">
You can also attach functions to a click event:
object.addEventListener("click", function(){ ... });
It seems like what you're asking is very basic, so I am not sure if I'm being helpful.

Categories