So, I have a JavaScript issue that is probably as old as time, but I can't seem to figure it out. I have a form that takes up the whole page and has next and back buttons so the user can travel back and forth through the questions. (Single page app, no full refresh. If there was this would be easy!)
One of the requirements is to have a timeout function. So after they are idle after a cretin amount of time it logs them out to protect their information. The issue is that my interval keeps adding a new one every time I flip back and forth new pages. Thus creating multiple timers and insanity.
My code kind of looks like this.
var idle = 0;
var idleIntervalTimer = setInterval(timerIncrement, 1000); //This is what keeps being called
var counter = 0;
var timeout = 30;
var count = 20;
var resetCount = 20;
document.onmousemove = function() {
if(app.timer.get('available') === 'Yes'){ //I'm just using this to make sure that they have not already been logged out
count = resetCount;
idle = 0;
app.warning.close();
}
};
function timerIncrement() {
if(app.timer.get('available') === 'Yes'){
console.log(idle);
idle = idle + 1;
if (idle > 19 && count >= 0) {
count = count;
app.warning.show({ //This is just a warning message that shows up at the top of the page counting down before the app closes.
message: 'Logging out in <span class="warn">' + count + '</span> seconds.'
});
if(count != null || count >= 0){
count=count-1;
if (count === 0 && count != 'null'){
resetTimer();
app.workflow.resetWorkflow();
}
}
}
}
}
function resetTimer(){
count = resetCount;
idle = 0;
clearInterval(counter);
clearInterval(idleIntervalTimer);
app.warning.close();
}
I have tried a few different things. One being that I would set a blank variable at the top and use that blank variable to set the interval, but that didn't seem to work. I have also tried to detect if the interval was still active, but I don't really want to clear the interval, just not create another. Some help would be apprenticed very much!
I'm assuming that code gets called on every page flip. Don't set the interval timer immediately. You should create a function that will first clear it and then set.
For example do this instead:
var idleIntervalTimer;
function initIdleIntervalTimer(){
clearInterval(idleIntervalTimer);
idleIntervalTimer = setInterval(timerIncrement, 1000);
}
initIdleIntervalTimer();
Or try switching it the timer to the global scope, get rid of var idleIntervalTimer and do this:
clearInterval(window.idleIntervalTimer);
window.idleIntervalTimer = setInterval(timerIncrement, 1000);
It's a little ugly, but
window.clearInterval(localStorage['idleInterval'])
var idleIntervalTimer = setInterval(timerIncrement, 1000); //This is what keeps being called
window.localStorage['idleInterval'] = idleIntervalTimer;
EDIT: If you dont want to clear, wrap the entire init in a flag, eg
if (!window._intervalInitOccured){
window._intervalInitOccured = true;
//continue init
Related
I'm building a program that either counts down or up and I've got it working however I like to press count-up in the middle of count down or vice versa and I like the counter to stop and count up or vice versa. how do I achieve that? thanks a lot for your help :)
function myFunctionUp() {
var Timer = setInterval(function () {
i++;
document.getElementById("mydata").textContent = i;
if (i >= 21)
clearInterval(Timer);
if (i == 21){
document.getElementById("mydata").textContent = "Boom-up!";
}
}, 1000);
}
function myFunctionDown() {
var Timer = setInterval(function () {
i--;
document.getElementById("mydata").textContent = i;
if (i <= 0)
clearInterval(Timer);
if (i == 0){
document.getElementById("mydata").textContent = "Boom-down";
}
}, 1000);
}
Use a variable to keep track of the way to count. When a button is clicked, invert the value of the variable :
let countDown = 10;
let increment = -1;
function count() {
countDown += increment;
document.getElementById('container').innerText = countDown;
setTimeout(() => count(), 1000);
}
document.getElementById('btn').addEventListener('click', function () {
increment = -increment;
});
count();
Working stackblitz here
You typically never "take control" on the execution of another method. When you want to do that, the logic must be inverted. The function itself must ask if it should continue.
With an example : let's take a function which works in an infinite loop, that you want to be able to stop on demand. In some languages, you could run some code in a thread and interrupt the thread on demand. But even if it is possible, it is generally a bad idea to stop some code at the middle of its execution.
A better way of doing that is to create a "should Continue ?" piece of code at the end of the iteration. It could read a variable or call a method etc. When you want to stop the iteration, you just have to set this variable and you know that the infinite loop will stop graciously at the end of the current iteration
I'm a bit of a beginner with Javascript and am struggling to figure out how to use a function of one instance to trigger a function in another instance, and vice versa, both of the same class. Let me explain what I mean.
My project is to build a Pomodoro Clock in Javascript. Pomodoro is a process where you work for a specified time (25 minutes, for example), then take a short break (5 mins), and then repeat. The clocks should run back to back, indefinitely or until the user stops it.
I need the completion of one clock to trigger the beginning of the other one, and vice versa.
I built a working program using completely separate, slightly varying functions for each timer (with much redundancy). I tried to simplify my code by creating a class of Timers and building each one from that. That's where I've got stuck.
I have a function in my Timer class which, when the timer reaches zero, needs to call the other timer's countdown to begin (line 126 on Codepen). How can I do that?
Thank you for any help you can offer.
Here's my project on Codepen: https://codepen.io/lieberscott/pen/baRpgx?editors=1010
And my Javascript code below:
let session; // session Timer object instance
let btimer; // break Timer object instance
let s_off; // boolean for whether session timer is off or on
let s_timer; // reference to session timer HTML element
let s_stop; // reference to session stop HTML button
let s_increase; // reference to session increase HTML button
let s_decrease; // reference to session decrease HTML button
// same variables as above for break timer
let b_off;
let b_timer;
let b_stop;
let b_increase;
let b_decrease;
$(document).ready(function() {
s_off = true;
s_timer = $("#timer");
s_stop = $("#stop");
s_increase = $("#increase");
s_decrease = $("#decrease");
b_off = true;
b_timer = $("#breaktimer");
b_stop = $("#breakstop");
b_increase = $("#breakincrease");
b_decrease = $("#breakdecrease");
session = new Timer(1, 60, s_off, s_timer, s_stop, s_increase, s_decrease);
btimer = new Timer(5, 60, b_off, b_timer, b_stop, b_increase, b_decrease);
// increase session minutes
$(s_increase).on("click", function() {
if (session.off) {
session.min++;
session.sec = 00;
s_timer.html(session.min + ":" + session.sec);
}
});
// decrease session minutes
$(s_decrease).on("click", function() {
if (session.off) {
if (session.min > 1) {
session.min--;
}
session.sec = 00;
s_timer.html(session.min + ":" + session.sec);
}
});
// increase break minutes
$(b_increase).on("click", function() {
if (btimer.off) {
btimer.min++;
btimer.sec = 00;
b_timer.html(btimer.min + ":" + btimer.sec);
}
});
// decrease break minutes
$(b_decrease).on("click", function() {
if (btimer.off) {
if (btimer.min > 1) {
btimer.min--;
}
btimer.sec = 00;
b_timer.html(btimer.min + ":" + btimer.sec);
}
});
// begin session timer by clicking on the timer itself
$(s_timer).on("click", function() {
session.time();
});
// stop session timer
$(s_stop).on("click", function() {
session.off = true;
session.stopClock(session.intervalFunction);
});
// stop break timer
$(b_stop).on("click", function() {
btimer.off = true;
btimer.stopClock(btimer.intervalFunction);
});
});
class Timer {
constructor(min, sec, off, disp, stopButton, increaseButton, decreaseButton) {
this.min = min; // minutes
this.minsSet = min; // minutes again, this will be used to reset the timer
this.sec = sec;
this.off = off; // boolean saying whether timer is off or not
this.disp = disp; // HTML display
this.stopButton = stopButton;
this.increaseButton = increaseButton;
this.decreaseButton = decreaseButton;
this.func;
}
time() { // function fired when the timer is clicked
if (this.off) {
this.off = false;
this.func = this.intervalFunc();
}
}
intervalFunc() { // set the interval of the timer
setInterval(function() {this.countdown();}, 1000); // ERROR HERE
}
countdown() { // interval to complete for duration of timer
// check if clock reaches zero
if (this.sec == 0) {
this.min--;
this.sec = 60;
if (this.min < 0) {
this.min = this.minsSet;
this.sec = 0;
this.off = true;
this.time(); // this needs to trigger, not this.time(), but the OTHER object's time() function
this.stopClock(this.func); // clearInterval() function below
}
}
// if clock is not at 0:00, display new time
this.sec--;
let m = this.min.toString();
let s;
if (this.sec < 10) {
s = "0" + this.sec.toString()
}
else {
s = this.sec.toString();
}
this.disp.html(m + ":" + s);
}
stopClock() {
clearInterval(this.func);
}
}
1) I tried your code and fixed some bugs, your setInterval issue is because "this" points to window object there.
2) for calling the other object time() method, first off you need something to know which object you are working with, so I have added a type variable to the class and then in the countdown function i have added a check.
Changes are in this pen :
https://codepen.io/yaduvanshi/pen/dJRdeR?editors=0010
intervalFunc() { // set the interval of the timer
var that =this;
setInterval(function() {that.countdown();}, 1000); // ERROR HERE
}
I think the solution that you are looking for, is the .bind() or the .call() that Javascript provides you with (Function.prototype.bind()). For example, the .bind() function takes the specific object instance as argument. You can read up on the same here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
I want the animation to stop when the number of clicks reach certain number.
Why the first snippet works, and the second one doesn't.
The only difference is where clearInterval is called.
This one works --- stops the animation.
var intervalId = setInterval(moveHeading, intervalLength);
var intervalLength = 120;
var clicks = 0;
$("#heading").click(function () {
clearInterval(intervalId);
clicks++;
intervalLength /= 2;
if (clicks >= 3) {
$("#heading").text("You Win!");
} else {
$("#heading").text(clicks);
intervalId = setInterval(moveHeading, intervalLength);
}
});
However this one one won't work --- doesn't stop animation.
var intervalId = setInterval(moveHeading, intervalLength);
var intervalLength = 120;
var clicks = 0;
$("#heading").click(function () {
clicks++;
intervalLength /= 2;
if (clicks > 3) {
clearInterval(intervalId);
$("#heading").text("You Win!");
} else {
$("#heading").text(clicks);
intervalId = setInterval(moveHeading, intervalLength);
}
});
On the first one, you clearInterval, then overwrite intervalId when you call setInterval, so you will never have a setInterval that is still running, where you no longer know the intervalId.
On the second one, if clicks <= 3, then you will overwrite intervalId, but you never cleared the running setInterval, so it will continue to run, and you will never be able to clearInterval on it since you don't know it's id (you will just be able to clear the interval for whatever new interval you created).
The setInterval() method will execute the function once every 1 second, just like a digital watch.
we have to use clearInterval() to stop the time.
It clears a timer set with the setInterval() method.
You must have to use it, in order to stop your animations.
I want a counter which reset in specific interval of time. I wrote this code. When I refresh the page it is executing perfectly. But as time passes the timer goes really fast, skipping seconds. Any idea why this is happening.
function countdown_new() {
window.setInterval(function () {
var timeCounter = $("b[id=show-time]").html();
var updateTime = eval(timeCounter) - eval(1);
$("b[id=show-time]").html(updateTime);
if (updateTime == 0) {
//window.location = ("ajax_chart.php");
$("b[id=show-time]").html(5);
clearInterval(countdown_new());
// countdown_new();
//my_ajax();
}
}, 1000);
}
window.setInterval(function () {
countdown_new();
}, 5000)
HTML
Coundown in 5 seconds
The issue is because you are not clearing the previous timer before starting a new one, so you start a new one for each iteration. To clear the timer you should save a reference to it, and pass that to clearInterval, not a function reference.
Also, note that your pattern of using multiple intervals for different operations can lead to overlap (where two intervals are acting at the same time and cause odd behaviour). Instead, use setTimeout for the initial 5 second delay and then chain further calls to stop this overlap.
Try this:
var timer;
function countdown_new() {
timer = setInterval(function () {
var $showTime = $("#show-time")
var updateTime = parseInt($showTime.text(), 10) - 1;
$showTime.html(updateTime);
if (updateTime == 0) {
$showTime.html('5');
clearInterval(timer);
setTimeout(countdown_new, 5000);
}
}, 1000);
}
setTimeout(countdown_new, 5000);
Example fiddle
Note that you should use the # selector to select an element by its id attribute, and you should never use eval - especially not for type coercion. To convert a value to an integer use parseInt().
You are calling window.setInterval(), which schedules a function call to countdown_new() ever 5 seconds without stop.
Compounding the problem, you are calling countdown_new() again inside your clear interval.
You need to call setInterval just once to continuously execute a function every 5 seconds.
If you want to cancel an interval timer, you need do to this:
var intervalObj = setInterval(function() { ... }, 5000);
clearInterval(intervalObj);
Yes clearinterval does the trick.
function countdown_new(){
t = window.setInterval(function() {
var timeCounter = $("b[id=show-time]").html();
var updateTime = eval(timeCounter)- eval(1);
$("b[id=show-time]").html(updateTime);
if(updateTime == 0){
//window.location = ("ajax_chart.php");
$("b[id=show-time]").html(5);
clearInterval(t);
// countdown_new();
my_ajax();
}
}, 1000);
}
I've made a jQuery player for images Demo Link.
It changes the screens with provided intervals and draws touches on it. Now, I want to implement pouse, play functionality.
When I click on play button to stop screen playing, I call FlowPlaye.stop() method:
FlowPlayer.prototype.stop = function() {
$(".fp-pause").removeClass("fp-pause").addClass("fp-play");
clearInterval(this.screenIntervalId);
clearInterval(this.timeIntervalId);
clearInterval(this.touchIntervalId);
$('.fp-progress').stop();
this.isAnimated = false;
return false;
}
And at the second time FlowPlayer.play():
FlowPlayer.prototype.play = function() {
var fp = this; // Obj refers to the FlowPlayer itself such as "this"
fp.isAnimated = true;
console.log(typeof this.screenIndex)
console.log(this.screenIndex)
fp.screenIndex = typeof this.screenIndex == 'number' ? this.screenIndex : 0;
fp.render(fp.screens[fp.screenIndex]);
fp.initTimeline(fp.duration);
fp.screenIntervalId = setInterval(function() {
if (fp.screenIndex == fp.screens.length - 1) {
console.log("the end of screens");
clearInterval(fp.screenIntervalId)
return;
}
++fp.screenIndex;
fp.render(fp.screens[fp.screenIndex]);
}, fp.screens[fp.screenIndex].delay)
}
The problem is that when I do this, the screen playing intervals are messing (try to stop video at 20th second and restore). I need to save state of player, but I don't know how.
I think using 3 different timers is making this unnecessary difficult. If you refactor it into 1 unified timer, pausing (and other playback controls) would be quite easy.
Separate your keyframe events into separate functions:
function setImage(img) {...}
function showTouch(x, y) {...}
function hideTouch() {...}
On startup, convert your screens array to something like this:
var keyframes = [
{ time:0, func:setImage, args:['http://...']},
{ time:1000, func:showTouch, args:[10, 30]},
{ time:3000, func:hideTouch, args:[]},
...
];
Set up a single timer for playback:
var time = 0,
next = 0,
isPaused = false,
interval;
function timer() {
if (isPaused) {
return;
}
var nextKeyframe = keyframes[next];
time += 100;
if (time >= nextKeyframe.time) {
nextKeyframe.func.apply(this, nextKeyframe.args);
next += 1;
if (next === keyframes.length) {
clearInterval(interval);
}
}
}
Now, you have an easily controllable playback:
// play / replay - reset time and next, then start the timer
time = 0;
next = 0;
interval = setInterval(timer, 100);
// seek - just set a new time, and find the next keyframe
time = 1500;
for (next = 0; keyframes[next].time < time && next < keyframes.length; next++) {}
// pause - the timer stays on, but won't do anything
isPaused = true;
// stop
clearInterval(interval);
Note: The snippets are untested, may have some typos in them. I just wanted to demonstrate the process of making it cleaner / more controllable.