How to prevent setInterval from being called multiple times simultaneously? - javascript

Is there a way to make the setInterval work only once at a time?
My code should work this way:
You manually input the number of the seconds;
The "div" works as a timer and runs till reaching 0.
If you click "Click me" once, it will work as expected.
However, if you click the button multiple times fast enough, the even listener will be working simultaneously as many times as you clicked.
let button = document.getElementById('button');
let input = document.getElementById('input');
let timer = document.getElementById('timer');
function timerRunner () {
let startTime = input.value;
if (startTime <= 0) {
console.log('The number is <=0')
} else {
do {
timer.textContent = startTime;
let counter = startTime;
let interval = setInterval(()=> {
console.log(counter);
counter--;
timer.textContent = counter;
if (counter === 0) {
clearInterval(interval);
}
}, 1000);
} while (typeof (timer.textContent) == "number");
}
}
button.addEventListener('click', timerRunner)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.centeralizer {
display: block;
width: 200px;
margin: 20vh auto;
}
#input {
display: block;
margin-bottom: 35px;
width: 150px;
}
#button {
display: inline-block;
margin-right: 35%;
}
#timer {
display: inline-block;
}
</style>
<script src="index.js" defer></script>
</head>
<body>
<div class="centeralizer">
<input id="input" placeholder="Input the number" type="number">
<button id="button">Click me</button>
<div id="timer">0</div>
</div>
</body>
</html>

You should store the running interval in the upper scope and clear it if the button is pressed again.
const button = document.getElementById('button');
const input = document.getElementById('input');
const timer = document.getElementById('timer');
let interval = null;
function startTimer() {
const startTime = +input.value;
if (startTime <= 0) {
console.log('The number is <= 0');
return;
}
let counter = startTime;
timer.textContent = startTime;
if (interval) clearInterval(interval);
interval = setInterval(() => {
timer.textContent = --counter;
console.log(counter);
if (counter === 0) clearInterval(interval);
}, 1000);
}
button.addEventListener('click', startTimer)

You can add a boolean variable isRunning that tracks whether the timer is running. You set it on when the timer is running and set it off when the timer is done. Check if it's on when the button is clicked, and return if it is. You can prevent multiple instances of the timer being triggered.
let button = document.getElementById('button');
let input = document.getElementById('input');
let timer = document.getElementById('timer');
var isRunning = false;
function timerRunner () {
if(isRunning) {
return; //return if timer is already running
}
isRunning = true; //specify that the timer is running
let startTime = input.value;
if (startTime <= 0) {
console.log('The number is <=0')
} else {
do {
timer.textContent = startTime;
let counter = startTime;
let interval = setInterval(()=> {
console.log(counter);
counter--;
timer.textContent = counter;
if (counter === 0) {
isRunning = false; //specify that the timer is not running and can be run again
clearInterval(interval);
}
}, 1000);
} while (typeof (timer.textContent) == "number");
}
}
button.addEventListener('click', timerRunner)

Related

How to run a function when scroll reaches a certain component?

There are some components stacked on top of each other and the last component has a timer. I want the timer to start only when that component is visible on screen or when scroll is reached to that component. [REPL]
let count_val = 80;
let count = 0;
function startTimer() {
let count_interval = setInterval(() => {
count += 1;
if(count >= count_val) {
clearInterval(count_interval);
}
}, 100);
}
// check if scroll reached to component and run below function.
startTimer();
How do I achieve this?
Like commented this can be achieved using Intersection Observer and an action
REPL
<script>
let count_val = 80;
let count = 0;
function timer(node) {
let interval
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
interval = setInterval(() => {
count += 1;
if(count === count_val) {
clearInterval(interval);
}
}, 100);
} else {
clearInterval(interval)
count = 0
}
})
})
observer.observe(node)
return {
destroy: () => observer.disconnect()
}
}
</script>
<div use:timer>
<p>
Counter - {count}
</p>
</div>
<style>
div {
height: 100vh;
display: grid;
place-items: center;
background-color: teal;
color: #0a0a0a;
font-size: 4rem;
}
</style>

Make a button disable at clicked but enable until countdown isn't zero

function counter2(){
var counter =10;
var on=document.getElementById("bet");
setInterval(function(){
counter--;
if(counter==9){
on.innerHTML = "";
}
if(counter>=0){
id=document.getElementById("res");
var bet=document.getElementById("pts");
var beth=document.getElementById("beth");
var betv=document.getElementById("betv");
id.innerHTML="Bet within " +counter + " seconds";;
beth.disabled = false; //Beth is both Function and button //
beth.style.backgroundColor = "#4CAF50";
betv.disabled = false;
betv.style.backgroundColor = "#008CBA";
beth.style.cursor = 'pointer';
betv.style.cursor = 'pointer';
}
if(counter==0){
counter1();
}
}, 1000);
}
I am making a function where a button will be disabled after clicked but the above counter automatically enables the buttons but i want both of them to be executed i. E. Disable a button after clicked and also disable if the timer is going on and also to enable the buttons if the button is not clicked
document.addEventListener('DOMContentLoaded', () => {
const myButton = document.getElementById('my-button');
const status = document.getElementById('status');
let counter = 10;
let timerHandle;
myButton.addEventListener('click', () => {
status.innerText = `Button will enable in ${counter} seconds`;
myButton.disabled = true;
timerHandle = setInterval(() => {
counter--;
status.innerText = `Button will enable in ${counter} seconds`;
if (counter === 0) {
clearInterval(timerHandle);
status.innerText = `Timer cleared.`;
myButton.disabled = false;
}
}, 1000);
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
p {
color: red;
}
</style>
</head>
<body>
<div>
<p id="status"></p>
</div>
<div>
<button id="my-button">Button</button>
</div>
<script src="script.js"></script>
</body>
</html>
You could use:
var elem = document.getElementById('button');
elem.addEventListener('click',buttonClicked);
function buttonClicked(){
//do stuff
console.log("clicked")
elem.disabled = true;
setTimeout(() => {elem.disabled =false}, 500);
}

How to get Javascript to run after a video has fully loaded in the DOM

How do I ensure my Javascript always loads after an embedded Wistia video loads?
I have a page that has a Wistia video embedded on it, and some Javascript that is trying to do the following:
Detect the play button of the video (and full screen button)
Listen for when the play button is clicked (or clicked again, aka paused)
Start a timer when the play button is clicked
Pause the timer when the video is paused
After enough time passes, reveal some hidden content
I had this working during testing, but later I realized it was failing on most occasions because my Javascript was running before the Wistia video was loaded, therefore, it wasn't finding the video elements I need to add eventlisteners to.
I tried to look up solutions to defer javascript or wait for the whole page to load, but it isn't working. Any help you could provide to get this timer working 100% of the time would be appreciated!!
Please view my code below, or look at the JSFiddle right here: https://jsfiddle.net/skuq16t3/
HTML
<div id="video">
<script src="https://fast.wistia.com/embed/medias/wd2eroj8k3.jsonp" async></script><script src="https://fast.wistia.com/assets/external/E-v1.js" async></script>
<div class="wistia_responsive_padding" style="padding: 56.25% 0 0 0; position: relative;">
<div class="wistia_responsive_wrapper" style="height: 100%; left: 0; position: absolute; top: 0; width: 100%;">
<div class="wistia_embed wistia_async_wd2eroj8k3 videoFoam=true" style="height: 100%; position: relative; width: 100%;">
<div class="wistia_swatch" style="height: 100%; left: 0; opacity: 0; overflow: hidden; position: absolute; top: 0; transition: opacity 200ms; width: 100%;">
<img style="filter: blur(5px); height: 100%; object-fit: contain; width: 100%;" src="https://fast.wistia.com/embed/medias/wd2eroj8k3/swatch" alt="" aria-hidden="true" />
</div>
</div>
</div>
</div>
</div>
<div id="countdowntimer">
<p class="left normaltext countdowntext">Your exclusive invitation will be unlocked in <span class="countdowntimerspan"><span id="minutesleft">16</span>:<span id="secondsleft">50</span></span></p>
<p class="stopwatchscreen"><span id="minutes">00</span>:<span id="seconds">00</span>:<span id="tens">00</span></p>
</div>
<div id="hiddencontentunlockedbytimer">
<p>Hidden content here</p>
</div>
CSS
#video {
max-width: 400px;
}
#countdowntimer {
background: #fafafa;
padding: 20px;
text-align: center;
border: 3px solid #b81937;
border-radius: 5px;
margin: 40px 20px;
font-size: 24px;
box-shadow: 5px 5px 10px #aaa;
}
#hiddencontentunlockedbytimer {
display: none;
}
Javascript
jQuery(document).ready(function() {
console.log('The page is now loaded');
var Interval;
var minutes = 0;
var seconds = 0;
var tens = 0;
var appendTens = document.getElementById('tens');
var appendSeconds = document.getElementById('seconds');
var appendMinutes = document.getElementById('minutes');
// make variables for how much time needs to pass before the video is finished
var minutesUntilFinished = 16; // change this
var secondsUntilFinished = 50; // change this
// make variables for the countdown timer
var appendSecondsCD = document.getElementById('secondsleft');
var appendMinutesCD = document.getElementById('minutesleft');
// This is the full screen button. Do nothing when this is clicked
var wistiaFullScreenButton = document.querySelector('#wistia_grid_29_main button.w-vulcan-v2-button.w-css-reset.w-css-reset-tree.w-css-reset-button-important');
// find the things that will start and pause the Wistia video
var wistiaBigPlayButton = document.querySelector('#wistia_grid_29_main button.w-big-play-button.w-css-reset-button-important.w-vulcan-v2-button');
var wistiaVideoArea = document.querySelector('#wistia_grid_29_main .w-vulcan--background.w-css-reset');
// test if different parts of the Wistia video were found and then clicked
if(wistiaFullScreenButton) {
console.log('The full screen button was found');
document.querySelector('#wistia_grid_29_main button.w-vulcan-v2-button.w-css-reset.w-css-reset-tree.w-css-reset-button-important').addEventListener('click', function() {
console.log('The Wistia full screen button was clicked');
});
} else {
console.log('The full screen button was NOT found');
}
if(wistiaBigPlayButton) {
console.log('The big Wistia play button was found');
document.querySelector('#wistia_grid_29_main button.w-big-play-button.w-css-reset-button-important.w-vulcan-v2-button').addEventListener('click', function() {
console.log('The big Wistia play button was clicked');
});
} else {
console.log('The big Wistia play button was NOT found');
}
if(wistiaVideoArea) {
console.log('The Wistia video area was found');
document.querySelector('#wistia_grid_29_main .w-vulcan--background.w-css-reset').addEventListener('click', function() {
console.log('The Wistia video area was clicked');
});
} else {
console.log('The Wistia video area was NOT found');
}
// figure out if a number is even or odd (for pausing the timer)
function isOdd(num) {
return num % 2;
}
// start the timer after the big button is pressed the first time
jQuery(wistiaBigPlayButton).one('click', function() {
console.log('The Big Wistia play button was clicked for the first time');
var BigPlayClickCount = 1;
// start the timer
clearInterval(Interval);
Interval = setInterval(startTimer, 10);
// pause the timer if the video is paused
wistiaBigPlayButton.onclick = function() {
// increase the click count by 1
BigPlayClickCount++;
console.log(BigPlayClickCount);
// if its an even number, start the timer
if(isOdd(BigPlayClickCount) == 1) {
clearInterval(Interval);
Interval = setInterval(startTimer, 10);
}
// if its an odd number, pause the timer
if(isOdd(BigPlayClickCount) == 0) {
clearInterval(Interval);
}
}
// pause the timer if the video is paused
wistiaVideoArea.onclick = function() {
// increase the click count by 1
BigPlayClickCount++;
console.log(BigPlayClickCount);
// if its an even number, start the timer
if(isOdd(BigPlayClickCount) == 1) {
clearInterval(Interval);
Interval = setInterval(startTimer, 10);
}
// if its an odd number, pause the timer
if(isOdd(BigPlayClickCount) == 0) {
clearInterval(Interval);
}
}
});
// also start the timer if they click on the video but not the big red button (both work)
jQuery(wistiaVideoArea).one('click', function() {
console.log("The Wistia video area was clicked for the first time");
var BigPlayClickCount = 1;
console.log(BigPlayClickCount);
// start the timer
clearInterval(Interval);
Interval = setInterval(startTimer, 10);
// pause the timer if the video is paused
wistiaBigPlayButton.onclick = function() {
// increase the click count by 1
BigPlayClickCount++;
console.log(BigPlayClickCount);
// if its an even number, start the timer
if(isOdd(BigPlayClickCount) == 1) {
clearInterval(Interval);
Interval = setInterval(startTimer, 10);
}
// if its an odd number, pause the timer
if(isOdd(BigPlayClickCount) == 0) {
clearInterval(Interval);
}
}
// pause the timer if the video is paused
wistiaVideoArea.onclick = function() {
// increase the click count by 1
BigPlayClickCount++;
// if its an even number, start the timer
if(isOdd(BigPlayClickCount) == 1) {
clearInterval(Interval);
Interval = setInterval(startTimer, 10);
}
// if its an odd number, pause the timer
if(isOdd(BigPlayClickCount) == 0) {
clearInterval(Interval);
}
}
});
function startTimer () {
tens++;
// add a 0 at the beginning if less than 9
if(tens <= 9) {
appendTens.innerHTML = "0" + tens;
}
if (tens > 9) {
appendTens.innerHTML = tens;
}
if (tens > 99) {
seconds++;
// subtract time from the seconds left
secondsUntilFinished--;
if (secondsUntilFinished > 9) {
document.getElementById("secondsleft").innerHTML = secondsUntilFinished;
}
if (secondsUntilFinished < 10) {
document.getElementById("secondsleft").innerHTML = "0" + secondsUntilFinished;
}
// once the seconds left reaches 0, start back over at 59
if (secondsUntilFinished < 1) {
secondsUntilFinished = 60;
}
if (secondsUntilFinished == 59) {
// subtract time from the minutes left counter
minutesUntilFinished--;
document.getElementById("minutesleft").innerHTML = minutesUntilFinished;
}
appendSeconds.innerHTML = "0" + seconds;
tens = 0;
appendTens.innerHTML = "0" + 0;
}
if (minutesUntilFinished <= 0) {
minutesUntilFinished = 0;
}
if (seconds > 9) {
appendSeconds.innerHTML = seconds;
}
if (seconds > 59) {
minutes++;
appendMinutes.innerHTML = "0" + minutes;
seconds = 0;
appendSeconds.innerHTML = "0" + 0;
appendTens.innerHTML = "0" + 0;
}
// after 1 minute show the countdown timer
if (minutes > 0) {
var countdowncheck = document.getElementById('countdowntimer');
if(countdowncheck) {
document.getElementById('countdowntimer').style.display = 'block';
}
}
// after a certain amount of time passes, show the hidden content
if (minutes > 1) {
// after 2 minutes show the hidden content
var hiddencontentcheck = document.getElementById('hiddencontentunlockedbytimer');
if(hiddencontentcheck) {
document.getElementById('hiddencontentunlockedbytimer').style.display = 'block';
}
}
}
});
Lots of code here. Probably could have shrunk this down a bit, but I found the answer. Here in the Wistia docs (https://wistia.com/support/developers/player-api) they have this code sample:
<script src="//fast.wistia.com/assets/external/E-v1.js" async></script>
<div class="wistia_embed wistia_async_abcde12345" style="width:640px;height:360px;"></div>
<script>
window._wq = window._wq || [];
_wq.push({ id: 'abcde12345', onReady: function(video) {
console.log("I got a handle to the video!", video);
}});
</script>
Basically, you put in the ID of your specific video and anything inside that function will only load after that video is loaded. Just remove the jquery bit at the beginning and replace it with that. Your updated code would look like this:
<script>
window._wq = window._wq || [];
_wq.push({ id: 'wd2eroj8k3', onReady: function(video) {
// All your code goes here
}});
</script>
Here is an updated Fiddle, just for reference: https://jsfiddle.net/8z41cLtj/

clear interval not stopping at desired point

I looked at some other similar problems on this site and could not fix this problem. Below is part of a pomodoro clock program that I'm making. The problem is that I'm unable to make this set interval method stop when the clock reaches 00:00.
here is my code:
var break_minutes = 0;
var ses_minutes = 0;
var ses_minutes_sec;
var display = document.querySelector('#time');
function increment_ses (id) {
ses_minutes = ses_minutes + 1;
document.getElementById("ses_value").innerHTML = ses_minutes ;
document.getElementById("timmer_circle").innerHTML = ses_minutes;
}
function decrement_ses (id) {
if (ses_minutes > 0) {
ses_minutes = ses_minutes - 10;
} if (ses_minutes < 0) {
ses_minutes = 0;
}
document.getElementById("ses_value").innerHTML = ses_minutes ;
document.getElementById("timmer_circle").innerHTML = ses_minutes;
}
function runTimer () {
var minutes = ses_minutes-1;
var seconds = 10;
var interval = setInterval(function () {
seconds = seconds -1;
if (seconds == 0) {
minutes --;
seconds = 10 -1;
}
function str_pad_left(string,pad,length) {
return (new Array(length+1).join(pad)+string).slice(-length);
}
var finalTime = str_pad_left(minutes,'0',2)+':'+str_pad_left(seconds,'0',2);
document.getElementById("timmer_circle").innerHTML= finalTime;
if (minutes == 0) {
if (seconds == 0) {
return clearInterval(interval);
}
}
},1000);
}
the HTML
<html>
<head>
<title>Pomodoro</title>
<script src="pomodoro.js"></script>
</head>
<body>
<style>
.timmer_circle
{
width: 300px;
height: 300px;
border-radius: 50%;
font-size: 50px;
color: #fff;
line-height: 300px;
text-align: center;
background: #000;
}
.break_length{width:100%;}
#decrement{float:left;width:100px;}
#break_value{text-align: center;padding-left: 100px;}
#increment{margin:0 auto;width:100px;}
.session_length{width:100%; margin-top: 10px;}
#decrement_ses{float:left;width:100px;}
#ses_value{padding-left: 100px;}
#increment_ses{margin:0 auto;width:100px;}
#start_but{margin-top: 20px;}
#pause_but{margin-top: 20px; margin-left: 2px;}
</style>
<table>
<tr>
<td>
<div class = "timmer_circle" id ="timmer_circle" value = ""> <span id = "time">Session</span> </div>
</td>
</tr>
</table>
<div class="session_length">
<button type="button" id = "decrement_ses" onClick = "decrement_ses(this.id);">ses/dec</button>
<button type="button" id = "ses_value" >0</button>
<button type="button" id = "increment_ses" onClick = "increment_ses(this.id);">ses/inc</button>
</div>
<div class="break_length">
<button type="button" id = "decrement" onClick = "decrement_break(this.id);">brk/dec</button>
<button type="button" id = "break_value" value = "" >0</button>
<button type="button" id = "increment" onClick = "increment_break(this.id);">brk/inc</button>
</div>
<button id ="start_but" onClick="runTimer();">START</button>
<button id ="pause_but">PAUSE</button>
</body>
</html>
So, there's a number of issues with your code, specifically how you're calculating seconds and minutes. Seconds are never zero when you hit your case statement the way you're doing it, because you test for zero at the start of your function and then reset them to 10-1.
I would move that check to the start of the function, as part of your initial conditional. There are still other issues, but this is to answer your specific question about the interval. The below code will exit as expected:
var ses_minutes = 1;
var minutes = ses_minutes-1;
var seconds = 10;
var interval = setInterval(function () {
seconds = seconds -1;
if (seconds == 0) {
if(minutes === 0){
return clearInterval(interval);
}
minutes --;
seconds = 10 - 1;
}
function str_pad_left(string,pad,length) {
return (new Array(length+1).join(pad)+string).slice(-length);
}
var finalTime = str_pad_left(minutes,'0',2)+':'+str_pad_left(seconds,'0',2);
document.getElementById("timmer_circle").innerHTML= finalTime;
},1000);
/* EDIT */
So taking a few minutes to think about how I'd do this if it were me, I'd break things up into functions a little differently, and I'd also use setTimeout. I'd also calculate based on elapsed time rather than assuming the interval or timeout tick will happen at exactly 1000ms, since that's not always reliable. The below is probably still not perfect (I've only done cursory testing) but closer to what I'd do if I were tasked with this:
const display = document.getElementById('timer');
const countDownFrom = 15000; // time in milliseconds to count from
let startTime = new Date().getTime();
function padNumber(num){
let str = num.toString();
return str.length > 1 ? str : '0' + str;
}
function getDisplay(milliseconds){
const seconds = milliseconds / 1000;
const displayMinutes = padNumber(Math.floor(seconds / 60));
const displaySeconds = padNumber(Math.floor(seconds % 60));
return displayMinutes + ':' + displaySeconds;
}
function tick(){
const currentTime = new Date().getTime();
const elapsedTime = currentTime - startTime;
// test to see if the timer has expired
if(countDownFrom - elapsedTime <= 0){
display.innerHTML = '00:00';
return;
}
display.innerHTML = getDisplay(countDownFrom - elapsedTime);
setTimeout(tick,1000);
}
display.innerHTML = getDisplay(countDownFrom);
setTimeout(tick, 1000);

Make countdown start after button is clicked

I'm creating a kid's game with a timer countdown that starts after the user clicks a button.
The code I'm using initiates the countdown without a problem, but I'd like the countdown to start only after the button is clicked.
Here is my code:
window.onload = function(){
(function(){
var counter = 5;
setInterval(function() {
counter--;
if (counter >= 0) {
span = document.getElementById("count");
span.innerHTML = counter;
}
if (counter === 0) {
alert('sorry, out of time');
clearInterval(counter);
}
}, 1000);
})();
}
JSFiddle
Thank you!
Lauren
Use click event of action link with id= "startClock"
$("#startClock").click( function(){
var counter = 5;
setInterval(function() {
counter--;
if (counter >= 0) {
span = document.getElementById("count");
span.innerHTML = counter;
}
if (counter === 0) {
alert('sorry, out of time');
clearInterval(counter);
}
}, 1000);
});
Create a function and add click lister to button to call that function. Do somthing like this.
function startTimer(){
var counter = 5;
setInterval(function() {
counter--;
if (counter >= 0) {
span = document.getElementById("count");
span.innerHTML = counter;
}
if (counter === 0) {
alert('sorry, out of time');
clearInterval(counter);
}
}, 1000);
}
function start()
{
document.getElementById("count").style="color:green;";
startTimer();
};
button
{
font-size:40px;
background-color:black;
color:white;
}
#count
{
margin-left:20px;
font-size:30px;
color:black;
}
<span id="count">5</span> seconds
<br>
<button onclick="start()">
START
</button>
Use on method $(selector).on('click',callback);
You are expecting the function get work when you click and in your code you are using onload event.
So use click() event of jQuery for your requirement.
$('#startClock').click(function(){
var counter = 5;
setInterval(function() {
counter--;
if (counter >= 0) {
span = document.getElementById("count");
span.innerHTML = counter;
}
if (counter === 0) {
alert('sorry, out of time');
clearInterval(counter);
}
}, 1000);
});
I just want to suggest you to put it in click handler of your selector instead of doing that on window load:
jQuery(function($){
$('#startClock').on('click', doCount);
});
function doCount(){
var counter = 5;
setInterval(function() {
counter--;
if (counter >= 0) {
span = document.getElementById("count");
span.innerHTML = counter;
}
if (counter === 0) {
alert('sorry, out of time');
clearInterval(counter);
}
}, 1000);
}
Demo # Fiddle
Also i need to suggest you that to use $(function(){}); dom ready function instead of window.onload = function(){}; just because dom ready does not wait for dom to get fully loaded to execute the script.
You are attaching your function in onLoad of the page, instead attach onClick
#count
{
font-size:30px;
color:green;
font-weight:bolder;
text-shadow:1px 1px 1px blue;
}
#text
{
font-size:30px;
color:blue;
font-weight:bolder;
}
button
{
font-size:25px;
background:black;
color:white;
border-radius:20px;
}
button:hover
{
transition:0.3s linear;
font-size:29px;
background:purple;
color:aliceblue;
border-radius:15px;
}
<span id="count">5</span> <span id="text">seconds</span>
<button onclick="start()">Start Clock</button>
<script>
function start(){
var counter = 5;
setInterval(function() {
counter--;
if (counter >= 0) {
span = document.getElementById("count");
span.innerHTML = counter;
}
if (counter === 0) {
alert('sorry, out of time');
clearInterval(counter);
}
}, 1000);
};
</script>

Categories

Resources