I'm trying to set up a timer to close a modal on an html page. The problem is that I need a global variable to be used in functions, and I can't re-assign a value to this variable.
I've looked for answers on many sites but none of them worked with my code. The value is correctly set when it is declared, but it can't be re-assigned later when I call it in functions (instead of simply reducing it's value).
No errors are detected in the browser's dev-tools.
var delayToCloseModal = 5;
function AnimateModal() {
// Each time this function is called
delayToCloseModal = 5; // HERE IS THE PROBLEM!!!
// delayToCloseModal is not set at 5 again
// If the modal isn't oppened
if (document.getElementById("modal").style.opacity < 0.01) {
// Open Modal
}
// Check every second if the timer is done, else reduce it by 1
var checkTimer = setInterval(CheckCounterDone, 1000);
function CheckCounterDone() {
if (delayToCloseModal > 0) {
// Reduce timer
delayToCloseModal--; // Strangely, this works really fine!
}
else {
// Stop checking timer
clearInterval(checkTimer);
// Close Modal
}
}
}
The "timer" system works really fine, the modal automatically close 5 seconds after its opening, but if the function is called again when the modal is already open, it won't reset the open timer to 5.
I found a solution for the problem! (It might be "homemade", but it works)
I created a Boolean variable to enable or disable the creation of a setInterval if one is already running. Here is the code :
var delayToCloseModal = 5;
var timerRunning = false; // Set false as default value to allow the creation of the first timer
function AnimateModal() {
// Each time this function is called
delayToCloseModal = 5;
// If the modal isn't oppened
if (document.getElementById("modal").style.opacity < 0.01) {
// Open Modal
}
// Check if a timer is already running
if (!timerRunning){
var checkTimer = setInterval(CheckCounterDone, 1000); // Create new timer
timerRunning = true; // Forbids to create another new timer
}
function CheckCounterDone() {
if (delayToCloseModal > 0) {
// Reduce timer
delayToCloseModal--;
}
else {
// Stop checking timer
clearInterval(checkTimer); // Delete the existing timer
timerRunning = false; // Allow to recreate another timer
// Close Modal
}
}
}
The modal now stays opens for 5 seconds since the last call of the function, and the timer is only reduced by one each second, even if you spam the call of the function, and close succesfully after the delay has been set to 0.
Thanks to anyone who has answered for your help! :)
Related
Im developing a game in JavaScript in which the user needs to give a key input (press spacebar) when a clock hand moves slightly more than usual.
Currently, I am using a setTimeout function that gives the user 1 second to give a key input after the clock hand has ticked (rotated by 10 degrees).
If the user correctly presses space when the clock hand moves more than usual (15 degrees), an indicator will flash green, otherwise it will flash red.
The problem I am running into is that once the user gives an input within 1 second of the hand moving, the indicator will not flash until AFTER that 1 second has passed (ie, if the user gives an input after 0.4 seconds, the indicator will not flash until 0.6 later)
I know this is because the indicator is set up in my setTimeout fuction, which will only execute the code after 1 second. I have tried to test for the user input outside of the setTimeout function but that way the user does not get 1 second to give a response.
I was wondering if there is a way around this problem or a better way to approach this?
//Get input after clock tick
setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
}
},1000);
Thanks for any help!
You'll need to keep a reference to your timer outside of the setTimeout callback and add a listener for the keypress with an interrupt callback which will clear the timeout if all conditions are met.
let timer = null;
let space_pressed = false;
function reset() {
timer = null;
space_pressed = false;
}
function interruptHandler(e) {
if (timer !== null) { // only look for spacebar if timer is running
space_pressed = e.key === ' ';
if (irregular_tick && space_pressed) {
// clear timeOut if successful
clearTimeout(timer);
reset();
flashScreenGreen();
}
}
}
document.body.addEventListener('keyup', interruptHandler);
timer = setTimeout(() => {
if (!space_pressed && irregular_tick) {
flashScreenRed();
}
// reset timer at end of callback ready for next run
reset();
}, 1000);
As a side note it looks like you've defined two separate flashScreenGreen() and flashScreenRed() functions. I'm guessing that they have similar if not identical logic. If that is the case you might want to consider defining a single utility flashScreen() function which accepts a color as a parameter.
function flashScreen(color) {
// logic utilizing 'color'
}
// usage
flashScreen('green');
flashScreen('#FF0000'); // red as hex
I think the clearTimeout function will help you here
// Hold the reference to the timer
const timeoutId = setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
//You can use the clearTimeout function to end the timer
clearTimeout(timeoutId);
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
//clear timeout, if you need it here too
clearTimeout(timeoutId);
}
},1000);
I have a toast notification system which displays a notification for 10 seconds before fading out. I want to add a functionality that pauses the countdown for fading out when the notification is hovered, and resumes when no longer hovering.
I'm trying to use the setInterval() function to do this, but I'm unsure how to later pause (clear) this interval. I know I can bind the setInterval to a variable, but these notifications are created dynamically, so I cannot bind them to a single variable.
In the example below, I store the setInterval() in a variable named ???, I would ideally bind this interval using the jQuery element ($(this)) as a variable so it's always unique, and can easily be cleared by passing the same jQuery element through the clearInterval() function.
Is there any way of doing this, or am I going about building this system all wrong?
// Start notification countdown
$.countDownNotification = function(notification) {
// IMPORTANT: Store the setInterval in a element-specific variable?
var ??? = setInterval( function() {
// Counts down from remaining seconds
var remaining = notification.attr('data-timer') + 1 - 1;
// Stores remaining seconds in data-attribute
notification.attr('data-timer', remaining);
// Remove notification when timer is on 0
if ( remaining == 0 ) {
notification.remove();
}
}, 1000);
}
// Pause on hover
$('.notification').on('mouseenter', function(e) {
// IMPORTANT: Clear the elemnt-specific interval
clearInterval(???);
});
// Resume when hover ends
$('.notification').on('mouseleave', function(e) {
var notification = $(this)
$.countDownNotification(notification);
});
You can store the interval on the notification via .data().
notification.data('int', setInterval(...
Then, in the event callbacks you can reference the interval via
$(this).data('int')
Also, note + 1 - 1 doesn't do anything meaningful.
Consider declaring a global variable.
// Start notification countdown
$.countDownNotification = function(notification) {
// IMPORTANT: Store the setInterval in a element-specific variable?
timer = setInterval( function() {
// Counts down from 10 and stores new value in data-attribute
notification.attr('data-timer', notification.attr('data-timer') - 1);
}, 1000);
// Remove notification when timer is on 0
if ( newRemaining == 0 ) {
notification.remove();
}
}
// `false` means no timer has been set
var timer = false;
// Pause on hover
$('.notification').on('mouseenter', function(e) {
// IMPORTANT: Clear the elemnt-specific interval
clearInterval( timer );
});
// Resume when hover ends
$('.notification').on('mouseleave', function(e) {
var notification = $(this)
$.countDownNotification(notification);
});
Another way to not set a global object is to return setInterval() by .countDownNotification.
// Start notification countdown
$.countDownNotification = function(notification) {
// IMPORTANT: Store the setInterval in a element-specific variable?
var id = setInterval( function() {
// Counts down from 10 and stores new value in data-attribute
notification.attr('data-timer', notification.attr('data-timer') - 1);
}, 1000);
// Remove notification when timer is on 0
if ( newRemaining == 0 ) {
notification.remove();
}
return id;
}
( function() {
// `false` means no timer has been set
var timer = false;
// Pause on hover
$('.notification').on('mouseenter', function(e) {
// IMPORTANT: Clear the elemnt-specific interval
clearInterval( timer );
});
// Resume when hover ends
$('.notification').on('mouseleave', function(e) {
var notification = $(this)
timer = $.countDownNotification(notification);
});
})();
Consider this C# question: Countdown timer increase on interaction?
I am in need of an equivalent for Javascript. That is, the following:
Problem
I need a callback to be called after some time T however if user interaction happens before the callback is executed then this time T must be increased by some number X. How is this modelled using Javascript?
Background (the why)
I have a page forward button which upon being clicked increases the page by 1. Increasing the page causes some hefty computations to happen so it'd be preferable to only switch page after some small time frame so that a user that's spamming the page button won't make the programs perf. go in the bin.
If I understand correctly, you can simply clear the timeout and set it again.
clearTimeout(timer);
timer = setTimeout(function(){}, 1000);
I made a quick codepen with an example. I hope it helps: https://codepen.io/daniti/pen/gjePdo
You can use a debounce function. It sets a timer and if an interation happens before timer expiration it deletes the old timer and creates a new one, effectively resets the time. Example:
function debounce(fn, delay) {
let timerId;
return function (...args) {
if (timerId)
clearTimeout(timerId);
timerId = setTimeout(() => {
fn(...args);
timerId = null;
}, delay);
}
}
You can use a timeout : https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
document.getElementById("spamMe").onclick = doLongAction;
var canDoLongAction = true;
var timeout;
function doLongAction() {
if (canDoLongAction) {
// prevent relaunch of action
canDoLongAction = false
// the action will become available in 2sec
timeout = window.setTimeout(
() => {canDoLongAction = true},
2000
)
// you do your long action
alert("hey")
} else {
// if clicked while the timeout is going reset it
window.clearTimeout(timeout);
timeout = window.setTimeout(
() => {canDoLongAction = true},
2000
)
}
}
<button id="spamMe">spam me!!</button>
in the example, the button is blocked until you stop clicking for 2 seconds
I have the following jquery code with setTimeout function to set a variable to false but it never works and the variable always remains to be true
for( var ff = 0; ; ff++)
{
if( dif == 0){
break;
}
if (locked){
// locked = false;
setTimeout( function(){ locked = false; },2000);
}
else{
LeftKeyPressed();
locked = true ;
setTimeout( function(){ locked = false; },3000);
dif--;
}
}
can anyone to help in how to set the locked variable to false after exactly two seconds from setting it to true.
Here's a fiddle of this issue.
Okey, since the requirements are what they are I dont know what exactly you want but this is closest I could manage to make: http://jsfiddle.net/db6gJ/
It calls method three times and gives you a change to do what you want to do when locked is false or true. Plus, it won't block the event loop (with that infinite for loop) so your site can make other tasks while timeOut call's are running.
javascript code also here:
var locked = true,
timesMax = 3,
timeCurrent = 0;
var inputLoop = function() {
var delay = 2000;
if(locked) {
// Do something when locked is true
console.log("locked is true!");
locked = false;
} else {
// Do something when locked is false
console.log("locked is false!");
locked = true;
delay = 3000;
}
timeCurrent++;
// call again after delay, but call only 3 times
if(timeCurrent < timesMax) {
setTimeout(inputLoop, delay);
}
};
// Launch it
inputLoop();
If you view your javascript console (in chrome: right mouse click -> inspect element -> console) it should print:
locked is true!
locked is false!
locked is true!
Also to be noted: your original code had alert('out'); when it's done, but to be honest, code will continue execution elsewhere while your "inputLoop" code is not doing anything else than waiting callback to be run and it's the way it should be.
If you wan't to know when it is called last time you could modify one of the previous lines to be:
// call again after delay, but call only 3 times
if(timeCurrent < timesMax) {
setTimeout(inputLoop, delay);
} else {
// Last call in this method
console.log('done!');
}
I have a simple javascript loop on my php page that just adds 1 to a value every second. Well, the loop runs every second, and increments the value.
var test = 0;
function go() {
test = test + 1;
setTimeout(go, 1000);
}
go();
This works fine.
Problem is, the PHP page this runs on is actually inside a div tag that refreshes every 10 seconds. After 10 seconds, the count goes haywire, adding 2 every second, then 3, then 4, etc.
How can I stop this?
Given that the problem appears to be multiple instances of your function running, increasing on each refresh/update of the page, I'd suggest adding a sanity-check to your function:
var test = 0;
var running = running || false;
function go() {
if (running) {
// if an instance of go() is already running, the function quits
return false;
}
else {
running = true; // as the test variable survives I assume this will, too
test = test + 1;
setTimeout(go, 1000);
}
}
go();
As it's probable that test is going to be overwritten every time the page updates, I'd suggest ensuring that the assignation isn't going to overwrite a pre-existing count:
var test = test || 0;
var running = running || false;
function go() {
if (running) {
// if an instance of go() is already running, the function quits
return false;
}
else {
var running = true; // as the test variable survives I assume this will, too
test = test + 1;
setTimeout(go, 1000);
}
}
go();
Bear in mind that you could simply use the existence of the test variable to determine whether the function is currently running or not, but because I don't know if you'll be using that for other purposes I've chosen to create another variable for that purpose (which should hold either true or false Boolean values).
Change: go() to if(!test){ go() }
You'll also have to mend your test variable. So var test = test || 0;