Related
I have a setInterval running a piece of code 30 times a second. This works great, however when I select another tab (so that the tab with my code becomes inactive), the setInterval is set to an idle state for some reason.
I made this simplified test case (http://jsfiddle.net/7f6DX/3/):
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.css("left", a)
}, 1000 / 30);
If you run this code and then switch to another tab, wait a few seconds and go back, the animation continues at the point it was when you switched to the other tab.
So the animation isn't running 30 times a second in case the tab is inactive. This can be confirmed by counting the amount of times the setInterval function is called each second - this will not be 30 but just 1 or 2 if the tab is inactive.
I guess that this is done by design so as to improve system performance, but is there any way to disable this behavior?
It’s actually a disadvantage in my scenario.
On most browsers inactive tabs have low priority execution and this can affect JavaScript timers.
If the values of your transition were calculated using real time elapsed between frames instead fixed increments on each interval, you not only workaround this issue but also can achieve a smother animation by using requestAnimationFrame as it can get up to 60fps if the processor isn't very busy.
Here's a vanilla JavaScript example of an animated property transition using requestAnimationFrame:
var target = document.querySelector('div#target')
var startedAt, duration = 3000
var domain = [-100, window.innerWidth]
var range = domain[1] - domain[0]
function start() {
startedAt = Date.now()
updateTarget(0)
requestAnimationFrame(update)
}
function update() {
let elapsedTime = Date.now() - startedAt
// playback is a value between 0 and 1
// being 0 the start of the animation and 1 its end
let playback = elapsedTime / duration
updateTarget(playback)
if (playback > 0 && playback < 1) {
// Queue the next frame
requestAnimationFrame(update)
} else {
// Wait for a while and restart the animation
setTimeout(start, duration/10)
}
}
function updateTarget(playback) {
// Uncomment the line below to reverse the animation
// playback = 1 - playback
// Update the target properties based on the playback position
let position = domain[0] + (playback * range)
target.style.left = position + 'px'
target.style.top = position + 'px'
target.style.transform = 'scale(' + playback * 3 + ')'
}
start()
body {
overflow: hidden;
}
div {
position: absolute;
white-space: nowrap;
}
<div id="target">...HERE WE GO</div>
For Background Tasks (non-UI related)
#UpTheCreek comment:
Fine for presentation issues, but still
there are some things that you need to keep running.
If you have background tasks that needs to be precisely executed at given intervals, you can use HTML5 Web Workers. Take a look at Möhre's answer below for more details...
CSS vs JS "animations"
This problem and many others could be avoided by using CSS transitions/animations instead of JavaScript based animations which adds a considerable overhead. I'd recommend this jQuery plugin that let's you take benefit from CSS transitions just like the animate() methods.
I ran into the same problem with audio fading and HTML5 player. It got stuck when tab became inactive.
So I found out a WebWorker is allowed to use intervals/timeouts without limitation. I use it to post "ticks" to the main javascript.
WebWorkers Code:
var fading = false;
var interval;
self.addEventListener('message', function(e){
switch (e.data) {
case 'start':
if (!fading){
fading = true;
interval = setInterval(function(){
self.postMessage('tick');
}, 50);
}
break;
case 'stop':
clearInterval(interval);
fading = false;
break;
};
}, false);
Main Javascript:
var player = new Audio();
player.fader = new Worker('js/fader.js');
player.faderPosition = 0.0;
player.faderTargetVolume = 1.0;
player.faderCallback = function(){};
player.fadeTo = function(volume, func){
console.log('fadeTo called');
if (func) this.faderCallback = func;
this.faderTargetVolume = volume;
this.fader.postMessage('start');
}
player.fader.addEventListener('message', function(e){
console.log('fader tick');
if (player.faderTargetVolume > player.volume){
player.faderPosition -= 0.02;
} else {
player.faderPosition += 0.02;
}
var newVolume = Math.pow(player.faderPosition - 1, 2);
if (newVolume > 0.999){
player.volume = newVolume = 1.0;
player.fader.postMessage('stop');
player.faderCallback();
} else if (newVolume < 0.001) {
player.volume = newVolume = 0.0;
player.fader.postMessage('stop');
player.faderCallback();
} else {
player.volume = newVolume;
}
});
There is a solution to use Web Workers (as mentioned before), because they run in separate process and are not slowed down
I've written a tiny script that can be used without changes to your code - it simply overrides functions setTimeout, clearTimeout, setInterval, clearInterval.
Just include it before all your code.
more info here
Both setInterval and requestAnimationFrame don't work when tab is inactive or work but not at the right periods. A solution is to use another source for time events. For example web sockets or web workers are two event sources that work fine while tab is inactive. So no need to move all of your code to a web worker, just use worker as a time event source:
// worker.js
setInterval(function() {
postMessage('');
}, 1000 / 50);
.
var worker = new Worker('worker.js');
var t1 = 0;
worker.onmessage = function() {
var t2 = new Date().getTime();
console.log('fps =', 1000 / (t2 - t1) | 0);
t1 = t2;
}
jsfiddle link of this sample.
Just do this:
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.stop(true,true).css("left", a);
}, 1000 / 30);
Inactive browser tabs buffer some of the setInterval or setTimeout functions.
stop(true,true) will stop all buffered events and execute immediatly only the last animation.
The window.setTimeout() method now clamps to send no more than one timeout per second in inactive tabs. In addition, it now clamps nested timeouts to the smallest value allowed by the HTML5 specification: 4 ms (instead of the 10 ms it used to clamp to).
For me it's not important to play audio in the background like for others here, my problem was that I had some animations and they acted like crazy when you were in other tabs and coming back to them. My solution was putting these animations inside if that is preventing inactive tab:
if (!document.hidden){ //your animation code here }
thanks to that my animation was running only if tab was active.
I hope this will help someone with my case.
I think that a best understanding about this problem is in this example: http://jsfiddle.net/TAHDb/
I am doing a simple thing here:
Have a interval of 1 sec and each time hide the first span and move it to last, and show the 2nd span.
If you stay on page it works as it is supposed.
But if you hide the tab for some seconds, when you get back you will see a weired thing.
Its like all events that didn't ucur during the time you were inactive now will ocur all in 1 time. so for some few seconds you will get like X events. they are so quick that its possible to see all 6 spans at once.
So it seams chrome only delays the events, so when you get back all events will occur but all at once...
A pratical application were this ocur iss for a simple slideshow. Imagine the numbers being Images, and if user stay with tab hidden when he came back he will see all imgs floating, Totally mesed.
To fix this use the stop(true,true) like pimvdb told.
THis will clear the event queue.
Heavily influenced by Ruslan Tushov's library, I've created my own small library. Just add the script in the <head> and it will patch setInterval and setTimeout with ones that use WebWorker.
Playing an audio file ensures full background Javascript performance for the time being
For me, it was the simplest and least intrusive solution - apart from playing a faint / almost-empty sound, there are no other potential side effects
You can find the details here: https://stackoverflow.com/a/51191818/914546
(From other answers, I see that some people use different properties of the Audio tag, I do wonder whether it's possible to use the Audio tag for full performance, without actually playing something)
I bring here a simple solution for anyone who is trying to get around this problem in a timer function, where as #kbtzr mentioned in another answer we can use the Date object instead of fixed increments to calculate the time that has passed since the beginning, which will work even if you are out from the application's tab.
This is the example HTML.
<body>
<p id="time"></p>
</body>
Then this JavaScript:
let display = document.querySelector('#time')
let interval
let time
function startTimer() {
let initialTime = new Date().getTime()
interval = setInterval(() => {
let now = new Date().getTime()
time = (now - initialTime) + 10
display.innerText = `${Math.floor((time / (60 * 1000)) % 60)}:${Math.floor((time / 1000) % 60)}:${Math.floor((time / 10) % 100)}`
}, 10)
}
startTimer()
That way, even if the interval value is increased for performance reasons of inactive tabs, the calculation made will guarantee the correct time. This is a vanilla code, but I used this logic in my React application, and you can modify it for wherever you need as well.
It is quite old question but I encountered the same issue.
If you run your web on chrome, you could read through this post Background Tabs in Chrome 57.
Basically the interval timer could run if it haven't run out of the timer budget.
The consumption of budget is based on CPU time usage of the task inside timer.
Based on my scenario, I draw video to canvas and transport to WebRTC.
The webrtc video connection would keep updating even the tab is inactive.
However you have to use setInterval instead of requestAnimationFrame but itt is not recommended for UI rendering though.
It would be better to listen visibilityChange event and change render mechenism accordingly.
Besides, you could try what Kaan Soral suggests and it should works based on the documentation.
I modified Lacerda's response, by adding a functioning UI.
I added start/resume/pause/stop actions.
const
timer = document.querySelector('.timer'),
timerDisplay = timer.querySelector('.timer-display'),
toggleAction = timer.querySelector('[data-action="toggle"]'),
stopAction = timer.querySelector('[data-action="stop"]'),
tickRate = 10;
let intervalId, initialTime, pauseTime = 0;
const now = () => new Date().getTime();
const formatTime = (hours, minutes, seconds) =>
[hours, minutes, seconds]
.map(v => `${isNaN(v) ? 0 : v}`.padStart(2, '0'))
.join(':');
const update = () => {
let
time = (now() - initialTime) + 10,
hours = Math.floor((time / (60000)) % 60),
minutes = Math.floor((time / 1000) % 60),
seconds = Math.floor((time / 10) % 100);
timerDisplay.textContent = formatTime(hours, minutes, seconds);
};
const
startTimer = () => {
initialTime = now();
intervalId = setInterval(update, tickRate);
},
resumeTimer = () => {
initialTime = now() - (pauseTime - initialTime);
intervalId = setInterval(update, tickRate);
},
pauseTimer = () => {
clearInterval(intervalId);
intervalId = null;
pauseTime = now();
},
stopTimer = () => {
clearInterval(intervalId);
intervalId = null;
initialTime = undefined;
pauseTime = 0;
},
restartTimer = () => {
stopTimer();
startTimer();
};
const setButtonState = (button, state, text) => {
button.dataset.state = state;
button.textContent = text;
};
const
handleToggle = (e) => {
switch (e.target.dataset.state) {
case 'pause':
setButtonState(e.target, 'resume', 'Resume');
pauseTimer();
break;
case 'resume':
setButtonState(e.target, 'pause', 'Pause');
resumeTimer();
break;
default:
setButtonState(e.target, 'pause', 'Pause');
restartTimer();
}
},
handleStop = (e) => {
stopTimer();
update();
setButtonState(toggleAction, 'initial', 'Start');
};
toggleAction.addEventListener('click', handleToggle);
stopAction.addEventListener('click', handleStop);
update();
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
background: #000;
}
.timer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5em;
}
.timer .timer-display {
font-family: monospace;
font-size: 3em;
background: #111;
color: #8F8;
border: thin solid #222;
padding: 0.25em;
}
.timer .timer-actions {
display: flex;
justify-content: center;
gap: 0.5em;
}
.timer .timer-actions button[data-action] {
font-family: monospace;
width: 6em;
border: thin solid #444;
background: #222;
color: #EEE;
padding: 0.5em;
cursor: pointer;
text-transform: uppercase;
}
.timer .timer-actions button[data-action]:hover {
background: #444;
border: thin solid #666;
color: #FFF;
}
<div class="timer">
<div class="timer-display"></div>
<div class="timer-actions">
<button data-action="toggle" data-state="initial">Start</button>
<button data-action="stop">Stop</button>
</div>
</div>
Note: This solution is not suitable if you like your interval works on the background, for example, playing audio or something else. But if you are confused for example about your animation not working properly when coming back to your page or tab, this is a good solution.
There are many ways to achieve this goal, maybe the "WebWorkers" is the most standard one but certainly, it's not the easiest and handy one, especially If you don't have enough Time, so you can try this way:
The basic concept:
build a name for your interval(or animation) and set your interval(animation), so it would run when user first time open your page : var interval_id = setInterval(your_func, 3000);
by $(window).focus(function() {}); and $(window).blur(function() {}); you can clearInterval(interval_id) everytime browser(tab) is deactived and ReRun your interval(animation) everytime browser(tab) would acive again by interval_id = setInterval();
Sample code:
var interval_id = setInterval(your_func, 3000);
$(window).focus(function() {
interval_id = setInterval(your_func, 3000);
});
$(window).blur(function() {
clearInterval(interval_id);
interval_id = 0;
});
Here's my rough solution
(function(){
var index = 1;
var intervals = {},
timeouts = {};
function postMessageHandler(e) {
window.postMessage('', "*");
var now = new Date().getTime();
sysFunc._each.call(timeouts, function(ind, obj) {
var targetTime = obj[1];
if (now >= targetTime) {
obj[0]();
delete timeouts[ind];
}
});
sysFunc._each.call(intervals, function(ind, obj) {
var startTime = obj[1];
var func = obj[0];
var ms = obj[2];
if (now >= startTime + ms) {
func();
obj[1] = new Date().getTime();
}
});
}
window.addEventListener("message", postMessageHandler, true);
window.postMessage('', "*");
function _setTimeout(func, ms) {
timeouts[index] = [func, new Date().getTime() + ms];
return index++;
}
function _setInterval(func, ms) {
intervals[index] = [func, new Date().getTime(), ms];
return index++;
}
function _clearInterval(ind) {
if (intervals[ind]) {
delete intervals[ind]
}
}
function _clearTimeout(ind) {
if (timeouts[ind]) {
delete timeouts[ind]
}
}
var intervalIndex = _setInterval(function() {
console.log('every 100ms');
}, 100);
_setTimeout(function() {
console.log('run after 200ms');
}, 200);
_setTimeout(function() {
console.log('closing the one that\'s 100ms');
_clearInterval(intervalIndex)
}, 2000);
window._setTimeout = _setTimeout;
window._setInterval = _setInterval;
window._clearTimeout = _clearTimeout;
window._clearInterval = _clearInterval;
})();
I was able to call my callback function at minimum of 250ms using audio tag and handling its ontimeupdate event. Its called 3-4 times in a second. Its better than one second lagging setTimeout
There is a workaround for this problem, although actually the tab must be made active in some window.
Make your inactive tab a separate browser window.
Don't make any other window maximized (unless the maximized window is behind yours).
This should give the browser the impression of always being active.
This is bit cumbersome, but also a quick win. Provided one has control over windows arrangement.
I have a setInterval running a piece of code 30 times a second. This works great, however when I select another tab (so that the tab with my code becomes inactive), the setInterval is set to an idle state for some reason.
I made this simplified test case (http://jsfiddle.net/7f6DX/3/):
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.css("left", a)
}, 1000 / 30);
If you run this code and then switch to another tab, wait a few seconds and go back, the animation continues at the point it was when you switched to the other tab.
So the animation isn't running 30 times a second in case the tab is inactive. This can be confirmed by counting the amount of times the setInterval function is called each second - this will not be 30 but just 1 or 2 if the tab is inactive.
I guess that this is done by design so as to improve system performance, but is there any way to disable this behavior?
It’s actually a disadvantage in my scenario.
On most browsers inactive tabs have low priority execution and this can affect JavaScript timers.
If the values of your transition were calculated using real time elapsed between frames instead fixed increments on each interval, you not only workaround this issue but also can achieve a smother animation by using requestAnimationFrame as it can get up to 60fps if the processor isn't very busy.
Here's a vanilla JavaScript example of an animated property transition using requestAnimationFrame:
var target = document.querySelector('div#target')
var startedAt, duration = 3000
var domain = [-100, window.innerWidth]
var range = domain[1] - domain[0]
function start() {
startedAt = Date.now()
updateTarget(0)
requestAnimationFrame(update)
}
function update() {
let elapsedTime = Date.now() - startedAt
// playback is a value between 0 and 1
// being 0 the start of the animation and 1 its end
let playback = elapsedTime / duration
updateTarget(playback)
if (playback > 0 && playback < 1) {
// Queue the next frame
requestAnimationFrame(update)
} else {
// Wait for a while and restart the animation
setTimeout(start, duration/10)
}
}
function updateTarget(playback) {
// Uncomment the line below to reverse the animation
// playback = 1 - playback
// Update the target properties based on the playback position
let position = domain[0] + (playback * range)
target.style.left = position + 'px'
target.style.top = position + 'px'
target.style.transform = 'scale(' + playback * 3 + ')'
}
start()
body {
overflow: hidden;
}
div {
position: absolute;
white-space: nowrap;
}
<div id="target">...HERE WE GO</div>
For Background Tasks (non-UI related)
#UpTheCreek comment:
Fine for presentation issues, but still
there are some things that you need to keep running.
If you have background tasks that needs to be precisely executed at given intervals, you can use HTML5 Web Workers. Take a look at Möhre's answer below for more details...
CSS vs JS "animations"
This problem and many others could be avoided by using CSS transitions/animations instead of JavaScript based animations which adds a considerable overhead. I'd recommend this jQuery plugin that let's you take benefit from CSS transitions just like the animate() methods.
I ran into the same problem with audio fading and HTML5 player. It got stuck when tab became inactive.
So I found out a WebWorker is allowed to use intervals/timeouts without limitation. I use it to post "ticks" to the main javascript.
WebWorkers Code:
var fading = false;
var interval;
self.addEventListener('message', function(e){
switch (e.data) {
case 'start':
if (!fading){
fading = true;
interval = setInterval(function(){
self.postMessage('tick');
}, 50);
}
break;
case 'stop':
clearInterval(interval);
fading = false;
break;
};
}, false);
Main Javascript:
var player = new Audio();
player.fader = new Worker('js/fader.js');
player.faderPosition = 0.0;
player.faderTargetVolume = 1.0;
player.faderCallback = function(){};
player.fadeTo = function(volume, func){
console.log('fadeTo called');
if (func) this.faderCallback = func;
this.faderTargetVolume = volume;
this.fader.postMessage('start');
}
player.fader.addEventListener('message', function(e){
console.log('fader tick');
if (player.faderTargetVolume > player.volume){
player.faderPosition -= 0.02;
} else {
player.faderPosition += 0.02;
}
var newVolume = Math.pow(player.faderPosition - 1, 2);
if (newVolume > 0.999){
player.volume = newVolume = 1.0;
player.fader.postMessage('stop');
player.faderCallback();
} else if (newVolume < 0.001) {
player.volume = newVolume = 0.0;
player.fader.postMessage('stop');
player.faderCallback();
} else {
player.volume = newVolume;
}
});
There is a solution to use Web Workers (as mentioned before), because they run in separate process and are not slowed down
I've written a tiny script that can be used without changes to your code - it simply overrides functions setTimeout, clearTimeout, setInterval, clearInterval.
Just include it before all your code.
more info here
Both setInterval and requestAnimationFrame don't work when tab is inactive or work but not at the right periods. A solution is to use another source for time events. For example web sockets or web workers are two event sources that work fine while tab is inactive. So no need to move all of your code to a web worker, just use worker as a time event source:
// worker.js
setInterval(function() {
postMessage('');
}, 1000 / 50);
.
var worker = new Worker('worker.js');
var t1 = 0;
worker.onmessage = function() {
var t2 = new Date().getTime();
console.log('fps =', 1000 / (t2 - t1) | 0);
t1 = t2;
}
jsfiddle link of this sample.
Just do this:
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.stop(true,true).css("left", a);
}, 1000 / 30);
Inactive browser tabs buffer some of the setInterval or setTimeout functions.
stop(true,true) will stop all buffered events and execute immediatly only the last animation.
The window.setTimeout() method now clamps to send no more than one timeout per second in inactive tabs. In addition, it now clamps nested timeouts to the smallest value allowed by the HTML5 specification: 4 ms (instead of the 10 ms it used to clamp to).
For me it's not important to play audio in the background like for others here, my problem was that I had some animations and they acted like crazy when you were in other tabs and coming back to them. My solution was putting these animations inside if that is preventing inactive tab:
if (!document.hidden){ //your animation code here }
thanks to that my animation was running only if tab was active.
I hope this will help someone with my case.
I think that a best understanding about this problem is in this example: http://jsfiddle.net/TAHDb/
I am doing a simple thing here:
Have a interval of 1 sec and each time hide the first span and move it to last, and show the 2nd span.
If you stay on page it works as it is supposed.
But if you hide the tab for some seconds, when you get back you will see a weired thing.
Its like all events that didn't ucur during the time you were inactive now will ocur all in 1 time. so for some few seconds you will get like X events. they are so quick that its possible to see all 6 spans at once.
So it seams chrome only delays the events, so when you get back all events will occur but all at once...
A pratical application were this ocur iss for a simple slideshow. Imagine the numbers being Images, and if user stay with tab hidden when he came back he will see all imgs floating, Totally mesed.
To fix this use the stop(true,true) like pimvdb told.
THis will clear the event queue.
Heavily influenced by Ruslan Tushov's library, I've created my own small library. Just add the script in the <head> and it will patch setInterval and setTimeout with ones that use WebWorker.
Playing an audio file ensures full background Javascript performance for the time being
For me, it was the simplest and least intrusive solution - apart from playing a faint / almost-empty sound, there are no other potential side effects
You can find the details here: https://stackoverflow.com/a/51191818/914546
(From other answers, I see that some people use different properties of the Audio tag, I do wonder whether it's possible to use the Audio tag for full performance, without actually playing something)
I bring here a simple solution for anyone who is trying to get around this problem in a timer function, where as #kbtzr mentioned in another answer we can use the Date object instead of fixed increments to calculate the time that has passed since the beginning, which will work even if you are out from the application's tab.
This is the example HTML.
<body>
<p id="time"></p>
</body>
Then this JavaScript:
let display = document.querySelector('#time')
let interval
let time
function startTimer() {
let initialTime = new Date().getTime()
interval = setInterval(() => {
let now = new Date().getTime()
time = (now - initialTime) + 10
display.innerText = `${Math.floor((time / (60 * 1000)) % 60)}:${Math.floor((time / 1000) % 60)}:${Math.floor((time / 10) % 100)}`
}, 10)
}
startTimer()
That way, even if the interval value is increased for performance reasons of inactive tabs, the calculation made will guarantee the correct time. This is a vanilla code, but I used this logic in my React application, and you can modify it for wherever you need as well.
It is quite old question but I encountered the same issue.
If you run your web on chrome, you could read through this post Background Tabs in Chrome 57.
Basically the interval timer could run if it haven't run out of the timer budget.
The consumption of budget is based on CPU time usage of the task inside timer.
Based on my scenario, I draw video to canvas and transport to WebRTC.
The webrtc video connection would keep updating even the tab is inactive.
However you have to use setInterval instead of requestAnimationFrame but itt is not recommended for UI rendering though.
It would be better to listen visibilityChange event and change render mechenism accordingly.
Besides, you could try what Kaan Soral suggests and it should works based on the documentation.
I modified Lacerda's response, by adding a functioning UI.
I added start/resume/pause/stop actions.
const
timer = document.querySelector('.timer'),
timerDisplay = timer.querySelector('.timer-display'),
toggleAction = timer.querySelector('[data-action="toggle"]'),
stopAction = timer.querySelector('[data-action="stop"]'),
tickRate = 10;
let intervalId, initialTime, pauseTime = 0;
const now = () => new Date().getTime();
const formatTime = (hours, minutes, seconds) =>
[hours, minutes, seconds]
.map(v => `${isNaN(v) ? 0 : v}`.padStart(2, '0'))
.join(':');
const update = () => {
let
time = (now() - initialTime) + 10,
hours = Math.floor((time / (60000)) % 60),
minutes = Math.floor((time / 1000) % 60),
seconds = Math.floor((time / 10) % 100);
timerDisplay.textContent = formatTime(hours, minutes, seconds);
};
const
startTimer = () => {
initialTime = now();
intervalId = setInterval(update, tickRate);
},
resumeTimer = () => {
initialTime = now() - (pauseTime - initialTime);
intervalId = setInterval(update, tickRate);
},
pauseTimer = () => {
clearInterval(intervalId);
intervalId = null;
pauseTime = now();
},
stopTimer = () => {
clearInterval(intervalId);
intervalId = null;
initialTime = undefined;
pauseTime = 0;
},
restartTimer = () => {
stopTimer();
startTimer();
};
const setButtonState = (button, state, text) => {
button.dataset.state = state;
button.textContent = text;
};
const
handleToggle = (e) => {
switch (e.target.dataset.state) {
case 'pause':
setButtonState(e.target, 'resume', 'Resume');
pauseTimer();
break;
case 'resume':
setButtonState(e.target, 'pause', 'Pause');
resumeTimer();
break;
default:
setButtonState(e.target, 'pause', 'Pause');
restartTimer();
}
},
handleStop = (e) => {
stopTimer();
update();
setButtonState(toggleAction, 'initial', 'Start');
};
toggleAction.addEventListener('click', handleToggle);
stopAction.addEventListener('click', handleStop);
update();
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
background: #000;
}
.timer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5em;
}
.timer .timer-display {
font-family: monospace;
font-size: 3em;
background: #111;
color: #8F8;
border: thin solid #222;
padding: 0.25em;
}
.timer .timer-actions {
display: flex;
justify-content: center;
gap: 0.5em;
}
.timer .timer-actions button[data-action] {
font-family: monospace;
width: 6em;
border: thin solid #444;
background: #222;
color: #EEE;
padding: 0.5em;
cursor: pointer;
text-transform: uppercase;
}
.timer .timer-actions button[data-action]:hover {
background: #444;
border: thin solid #666;
color: #FFF;
}
<div class="timer">
<div class="timer-display"></div>
<div class="timer-actions">
<button data-action="toggle" data-state="initial">Start</button>
<button data-action="stop">Stop</button>
</div>
</div>
Note: This solution is not suitable if you like your interval works on the background, for example, playing audio or something else. But if you are confused for example about your animation not working properly when coming back to your page or tab, this is a good solution.
There are many ways to achieve this goal, maybe the "WebWorkers" is the most standard one but certainly, it's not the easiest and handy one, especially If you don't have enough Time, so you can try this way:
The basic concept:
build a name for your interval(or animation) and set your interval(animation), so it would run when user first time open your page : var interval_id = setInterval(your_func, 3000);
by $(window).focus(function() {}); and $(window).blur(function() {}); you can clearInterval(interval_id) everytime browser(tab) is deactived and ReRun your interval(animation) everytime browser(tab) would acive again by interval_id = setInterval();
Sample code:
var interval_id = setInterval(your_func, 3000);
$(window).focus(function() {
interval_id = setInterval(your_func, 3000);
});
$(window).blur(function() {
clearInterval(interval_id);
interval_id = 0;
});
Here's my rough solution
(function(){
var index = 1;
var intervals = {},
timeouts = {};
function postMessageHandler(e) {
window.postMessage('', "*");
var now = new Date().getTime();
sysFunc._each.call(timeouts, function(ind, obj) {
var targetTime = obj[1];
if (now >= targetTime) {
obj[0]();
delete timeouts[ind];
}
});
sysFunc._each.call(intervals, function(ind, obj) {
var startTime = obj[1];
var func = obj[0];
var ms = obj[2];
if (now >= startTime + ms) {
func();
obj[1] = new Date().getTime();
}
});
}
window.addEventListener("message", postMessageHandler, true);
window.postMessage('', "*");
function _setTimeout(func, ms) {
timeouts[index] = [func, new Date().getTime() + ms];
return index++;
}
function _setInterval(func, ms) {
intervals[index] = [func, new Date().getTime(), ms];
return index++;
}
function _clearInterval(ind) {
if (intervals[ind]) {
delete intervals[ind]
}
}
function _clearTimeout(ind) {
if (timeouts[ind]) {
delete timeouts[ind]
}
}
var intervalIndex = _setInterval(function() {
console.log('every 100ms');
}, 100);
_setTimeout(function() {
console.log('run after 200ms');
}, 200);
_setTimeout(function() {
console.log('closing the one that\'s 100ms');
_clearInterval(intervalIndex)
}, 2000);
window._setTimeout = _setTimeout;
window._setInterval = _setInterval;
window._clearTimeout = _clearTimeout;
window._clearInterval = _clearInterval;
})();
I was able to call my callback function at minimum of 250ms using audio tag and handling its ontimeupdate event. Its called 3-4 times in a second. Its better than one second lagging setTimeout
There is a workaround for this problem, although actually the tab must be made active in some window.
Make your inactive tab a separate browser window.
Don't make any other window maximized (unless the maximized window is behind yours).
This should give the browser the impression of always being active.
This is bit cumbersome, but also a quick win. Provided one has control over windows arrangement.
I'm working for an artistic project and I'm struggling with a little thing I may have solved for Chrome but not for Firefox. I have a page where a text will be written by an A.I through a MySQL database. Quickly, the text will be out of its container so we managed to hide the scrollbar and follow the writing process without allowing the viewer to scroll up. Now I'm trying to allow the viewer to scroll up when the process is paused or done. You can see the test page here : http://82.223.18.239/writing7.php
I tried a few ways :
Using overflow:hidden all the time. This isn't working, the viewer can't scrollup when the writing process is paused or done.
switching from overflow:hidden to overflow:scroll when the writing is paused or done. In this case the window is forced back on the top of the page. This isn't good. I would like the view to stay at the bottom, where the writing is paused.
switching from overflow:hidden to overflow:scroll when the writing is paused or done. In this case the scrollbar is weirdly positioned and I didn't find any way to put it back on the right. It's glued to the myTables div, whatever I'm trying. When I'm trying to set it up on <body> this isn't working at all.
switching from overflow:hidden to overflow:scroll AND hidding the scrollbar. It maybe a bit messy but it works well for Chrome, by using ::-webkit-scrollbar {display: none;} but it doesn't work at all for Firefox.
To summarize, I would like the view to follow the writing process, like it does now, and I would like the user to be able to scroll up only when it's paused or done, allowing him/her to read the full text but I didn't find anyway to make it work. Could you help me a bit ?
Actually, I'm trying to change the overflow value of a #parent / #child div through this part of the showtext function, but it doesn't work either.
else {
get_data(skip);
$('#body').css('overflow', 'hidden')
$('#myTable').css('overflow-y', 'scroll').css('overflow-x', 'hidden')
}
Here's the actual full code :
<head>
<div id="header"></div>
<div id="body"></div>
<div id="footer"></div>
<div id="myTable"> <div>
<script type = "text/javascript" src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<style type="text/css">
#myTable{
width:"90%";
height:"100%";
min-width:250px;
white-space: pre-wrap;
word-wrap:break-word;
position:absolute;
border:solid 0px;
top:-500px;
left:320px;
right:320px;
bottom:0px;
font-size:103px;
font-family:"Times New Roman", Times, serif;
text-align:left
}
#body{
height:"100%";
min-width:250px;
}
#footer{
height:"0px";
}
::-webkit-scrollbar {display: none;}
</style>
</head>
<body>
<myTable>
<script type="text/javascript">
var skip = 0;
function get_data(index) {
$.ajax({
url : 'http://82.223.18.239/getData.php',
type : 'POST',
data: ({"skip":skip}),
success : function(data) {
if(data && data.trim()!='') {
skip = skip+1;
showText("#myTable", data, 0, 2);
}
else {
setTimeout(function () { get_data(skip); }, 30000);
}
},
error : function(request,error)
{
alert("Request error : "+JSON.stringify(request));
}
});
}
function showText(target, message, index, interval) {
if (index < message.length) {
$(target).append(message[index++]);
setTimeout(function () { showText(target, message, index, interval); }, interval);
$('#myTable').css('overflow', 'hidden').bind('DOMNodeInserted', function () {
this.scrollTop = this.scrollHeight;
});
}
else {
get_data(skip);
$('#body').css('overflow', 'hidden')
$('#myTable').css('overflow-y', 'scroll').css('overflow-x', 'hidden')
}
}
//var period = 10000; //NOTE: period is passed in milliseconds
get_data(skip);
//setInterval(page_refresh, period);
</script>
</myTable>
</body>
<footer>
<SCRIPT LANGUAGE="JavaScript">
var message = new Array();
message[0] = ""
var reps = 2;
var speed = 666;
var p = message.length;
var T = "";
var C = 0;
var mC = 0;
var s = 0;
var sT = null;
if (reps < 1) reps = 1;
function doIt() {
T = message[mC];
A();
}
function A() {
s++;
if (s > 8) { s = 1;}
if (s == 1) { document.title = '✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊🏿✊✊🏻✊✊🏼✊✊✊🏻✊✊🏼✊✊🏽✊✊🏾'+T+'✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊🏿✊✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊'; }
if (s == 2) { document.title = '☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠'+T+'☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️☠️'; }
if (s == 3) { document.title = '🌍🌎🌍🌎🌍🌎🌏🌍🌎🌍🌎🌍🌎🌏🌍🌎🌍🌎🌍🌎🌏'+T+'✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊🏿✊✊🏻✊✊🏼✊✊'; }
if (s == 4) { document.title = '✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊🏿✊✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊'+T+'⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔⛔'; }
if (s == 5) { document.title = '🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌'+T+'🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠🛠'; }
if (s == 6) { document.title = '💣💣💣💣💣💣💣💣💣💣💣💣💣💣💣💣💣💣💣💣'+T+'🖖🖖🏿🖖🖖🏼🖖🏾🖖🏼🖖🏿🖖🖖🏼🖖🏾🖖🖖🏿🖖🖖🏼🖖🏾🖖🏼'; }
if (s == 7) { document.title = '🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌'+T+'🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌🔌'; }
if (s == 8) { document.title = '✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊🏿✊✊🏻✊✊🏼✊✊🏽✊✊🏾✊✊🏿'+T+'⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳'; }if (C < (8 * reps)) {
sT = setTimeout("A()", speed);
C++;
}
else {
C = 0;
s = 0;
mC++;
if(mC > p - 1) mC = 0;
sT = null;
doIt();
}
}
doIt();
(function() {
var template = '✊☔☁☁☁☠⛔☁☁☁⏳☔⚠☁☁⛔⏳☠☁☁☁☁☁💣✊🏾'.split(''),
len = template.length,
chars, string, i, j, k,
pushOrHash = typeof window.history.pushState === 'function',
increase = function(n) {
return n < len - 1 ? n + 1 : 0;
},
update = function() {
chars = [];
j = k;
for (i=0; i<len; i++) {
j = increase(j);
chars[i] = template[j];
}
string = ['/', chars.join(''), '/'].join('');
k = increase(k);
if (pushOrHash) {
window.history.pushState(null, null, string);
} else {
window.document.location.hash = string;
}
setTimeout(update, 1000);
};
update();
})();
</script>
</script>
<script type="text/javascript">
function pageLoad()
{
alert('The image of external things possesses for us the ambiguous dimension that in external nature everything can be considered to be connected, but also as separated. The uninterrupted transformations of materials as well as energies brings everything into relationship with everything else and make one cosmos out of all the individual elements. On the other hand, however, the objects remain banished in the merciless separation of space; no particle of matter can share its space with another and a real unity of the diverse does not exist in spatial terms. And, by virtue of this equal demand on self-excluding concepts, natural existence seems to resist any application of them at all. Only to humanity, in contrast to nature, has the right to connect and separate been granted, and in the distinctive manner that one of these activities is always the presupposition of the other. By choosing two items from the undisturbed store of natural things in order to designate them as -separate-, we have already related them to one another in our consciousness, we have emphasized these two together against whatever lies between them. And conversely, we can only sense those things to be related which we have previously somehow isolated from one another; things must first be separated from one another in order to be together. Practically as well as logically, it would be meaningless to connect that which was not separated, and indeed that which also remains separated in some sense. The formula according to which both types of activity come together in human undertakings, whether the connectedness or the separation is felt to be what was naturally ordained and the respective alternative is felt to be our task, is something which can guide all our activity. In the immediate as well as the symbolic sense, in the physical as well as the intellectual sense, we are at any moment those who separate the connected or connect the separate. Georg Simmel from -Bridges and Doors- 1909̿');
}
pageLoad();
</script>
</footer>
You used to be able to use overflow: -moz-scrollbars-none; but it is depreciated in recent versions of firefox.
And attempting to change overflow-x or overflow-y has also been depreciated.
I recommend setting your right margin (or whichever side the scrollbar is on) to a value equal to the width of the scrollbar, only negative. Then when you want to show the scrollbar, all you have to do is adjust the margin.
Note that depending on your layout you may have to also set the other margins (top, bottom and left) in order for your content to look consistent and have appropriate spacing.
I finally find a way to do exactly what I wanted.
For Google Chrome and webkit browsers, you simply have to do overflow:hidden and hide the scrollbar with ::-webkit-scrollbar {display: none;}
For other browsers, you'll have to set -ms-overflow-style:none and use
var textareaWidth = document.getElementById("yourdiv").scrollWidth;
document.getElementById("yourdiv").style.width = textareaWidth + "px";
More specifically, for me it worked by rewriting the showtext function like this :
function showText(target, message, index, interval) {
if (index < message.length) {
$(target).append(message[index++]);
setTimeout(function () { showText(target, message, index, interval); }, interval);
$('#myTable').css('overflow', 'hidden').bind('DOMNodeInserted', function () {
this.scrollTop = this.scrollHeight;
});
}
else if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1){
get_data(skip);
$('#myTable').css('overflow', 'hidden')
document.getElementById("myTable").style.width = textareaWidth + "px";
}
else if (/MSIE 9/i.test(navigator.userAgent) || /rv:11.0/i.test(navigator.userAgent)){
get_data(skip);
$('#myTable').css('overflow', 'hidden').css('-ms-overflow-style', 'none')
document.getElementById("myTable").style.width = textareaWidth + "px";
}
else if (/MSIE 10/i.test(navigator.userAgent)){
get_data(skip);
$('#myTable').css('overflow', 'hidden').css('-ms-overflow-style', 'none')
document.getElementById("myTable").style.width = textareaWidth + "px";
}
else if (/Edge\/\d./i.test(navigator.userAgent)){
get_data(skip);
$('#myTable').css('overflow', 'hidden').css('-ms-overflow-style', 'none')
document.getElementById("myTable").style.width = textareaWidth + "px";
}
else {
get_data(skip);
$('#myTable').css('overflow', 'scroll')
}
}
I have a setInterval running a piece of code 30 times a second. This works great, however when I select another tab (so that the tab with my code becomes inactive), the setInterval is set to an idle state for some reason.
I made this simplified test case (http://jsfiddle.net/7f6DX/3/):
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.css("left", a)
}, 1000 / 30);
If you run this code and then switch to another tab, wait a few seconds and go back, the animation continues at the point it was when you switched to the other tab.
So the animation isn't running 30 times a second in case the tab is inactive. This can be confirmed by counting the amount of times the setInterval function is called each second - this will not be 30 but just 1 or 2 if the tab is inactive.
I guess that this is done by design so as to improve system performance, but is there any way to disable this behavior?
It’s actually a disadvantage in my scenario.
On most browsers inactive tabs have low priority execution and this can affect JavaScript timers.
If the values of your transition were calculated using real time elapsed between frames instead fixed increments on each interval, you not only workaround this issue but also can achieve a smother animation by using requestAnimationFrame as it can get up to 60fps if the processor isn't very busy.
Here's a vanilla JavaScript example of an animated property transition using requestAnimationFrame:
var target = document.querySelector('div#target')
var startedAt, duration = 3000
var domain = [-100, window.innerWidth]
var range = domain[1] - domain[0]
function start() {
startedAt = Date.now()
updateTarget(0)
requestAnimationFrame(update)
}
function update() {
let elapsedTime = Date.now() - startedAt
// playback is a value between 0 and 1
// being 0 the start of the animation and 1 its end
let playback = elapsedTime / duration
updateTarget(playback)
if (playback > 0 && playback < 1) {
// Queue the next frame
requestAnimationFrame(update)
} else {
// Wait for a while and restart the animation
setTimeout(start, duration/10)
}
}
function updateTarget(playback) {
// Uncomment the line below to reverse the animation
// playback = 1 - playback
// Update the target properties based on the playback position
let position = domain[0] + (playback * range)
target.style.left = position + 'px'
target.style.top = position + 'px'
target.style.transform = 'scale(' + playback * 3 + ')'
}
start()
body {
overflow: hidden;
}
div {
position: absolute;
white-space: nowrap;
}
<div id="target">...HERE WE GO</div>
For Background Tasks (non-UI related)
#UpTheCreek comment:
Fine for presentation issues, but still
there are some things that you need to keep running.
If you have background tasks that needs to be precisely executed at given intervals, you can use HTML5 Web Workers. Take a look at Möhre's answer below for more details...
CSS vs JS "animations"
This problem and many others could be avoided by using CSS transitions/animations instead of JavaScript based animations which adds a considerable overhead. I'd recommend this jQuery plugin that let's you take benefit from CSS transitions just like the animate() methods.
I ran into the same problem with audio fading and HTML5 player. It got stuck when tab became inactive.
So I found out a WebWorker is allowed to use intervals/timeouts without limitation. I use it to post "ticks" to the main javascript.
WebWorkers Code:
var fading = false;
var interval;
self.addEventListener('message', function(e){
switch (e.data) {
case 'start':
if (!fading){
fading = true;
interval = setInterval(function(){
self.postMessage('tick');
}, 50);
}
break;
case 'stop':
clearInterval(interval);
fading = false;
break;
};
}, false);
Main Javascript:
var player = new Audio();
player.fader = new Worker('js/fader.js');
player.faderPosition = 0.0;
player.faderTargetVolume = 1.0;
player.faderCallback = function(){};
player.fadeTo = function(volume, func){
console.log('fadeTo called');
if (func) this.faderCallback = func;
this.faderTargetVolume = volume;
this.fader.postMessage('start');
}
player.fader.addEventListener('message', function(e){
console.log('fader tick');
if (player.faderTargetVolume > player.volume){
player.faderPosition -= 0.02;
} else {
player.faderPosition += 0.02;
}
var newVolume = Math.pow(player.faderPosition - 1, 2);
if (newVolume > 0.999){
player.volume = newVolume = 1.0;
player.fader.postMessage('stop');
player.faderCallback();
} else if (newVolume < 0.001) {
player.volume = newVolume = 0.0;
player.fader.postMessage('stop');
player.faderCallback();
} else {
player.volume = newVolume;
}
});
There is a solution to use Web Workers (as mentioned before), because they run in separate process and are not slowed down
I've written a tiny script that can be used without changes to your code - it simply overrides functions setTimeout, clearTimeout, setInterval, clearInterval.
Just include it before all your code.
more info here
Both setInterval and requestAnimationFrame don't work when tab is inactive or work but not at the right periods. A solution is to use another source for time events. For example web sockets or web workers are two event sources that work fine while tab is inactive. So no need to move all of your code to a web worker, just use worker as a time event source:
// worker.js
setInterval(function() {
postMessage('');
}, 1000 / 50);
.
var worker = new Worker('worker.js');
var t1 = 0;
worker.onmessage = function() {
var t2 = new Date().getTime();
console.log('fps =', 1000 / (t2 - t1) | 0);
t1 = t2;
}
jsfiddle link of this sample.
Just do this:
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.stop(true,true).css("left", a);
}, 1000 / 30);
Inactive browser tabs buffer some of the setInterval or setTimeout functions.
stop(true,true) will stop all buffered events and execute immediatly only the last animation.
The window.setTimeout() method now clamps to send no more than one timeout per second in inactive tabs. In addition, it now clamps nested timeouts to the smallest value allowed by the HTML5 specification: 4 ms (instead of the 10 ms it used to clamp to).
For me it's not important to play audio in the background like for others here, my problem was that I had some animations and they acted like crazy when you were in other tabs and coming back to them. My solution was putting these animations inside if that is preventing inactive tab:
if (!document.hidden){ //your animation code here }
thanks to that my animation was running only if tab was active.
I hope this will help someone with my case.
I think that a best understanding about this problem is in this example: http://jsfiddle.net/TAHDb/
I am doing a simple thing here:
Have a interval of 1 sec and each time hide the first span and move it to last, and show the 2nd span.
If you stay on page it works as it is supposed.
But if you hide the tab for some seconds, when you get back you will see a weired thing.
Its like all events that didn't ucur during the time you were inactive now will ocur all in 1 time. so for some few seconds you will get like X events. they are so quick that its possible to see all 6 spans at once.
So it seams chrome only delays the events, so when you get back all events will occur but all at once...
A pratical application were this ocur iss for a simple slideshow. Imagine the numbers being Images, and if user stay with tab hidden when he came back he will see all imgs floating, Totally mesed.
To fix this use the stop(true,true) like pimvdb told.
THis will clear the event queue.
Heavily influenced by Ruslan Tushov's library, I've created my own small library. Just add the script in the <head> and it will patch setInterval and setTimeout with ones that use WebWorker.
Playing an audio file ensures full background Javascript performance for the time being
For me, it was the simplest and least intrusive solution - apart from playing a faint / almost-empty sound, there are no other potential side effects
You can find the details here: https://stackoverflow.com/a/51191818/914546
(From other answers, I see that some people use different properties of the Audio tag, I do wonder whether it's possible to use the Audio tag for full performance, without actually playing something)
I bring here a simple solution for anyone who is trying to get around this problem in a timer function, where as #kbtzr mentioned in another answer we can use the Date object instead of fixed increments to calculate the time that has passed since the beginning, which will work even if you are out from the application's tab.
This is the example HTML.
<body>
<p id="time"></p>
</body>
Then this JavaScript:
let display = document.querySelector('#time')
let interval
let time
function startTimer() {
let initialTime = new Date().getTime()
interval = setInterval(() => {
let now = new Date().getTime()
time = (now - initialTime) + 10
display.innerText = `${Math.floor((time / (60 * 1000)) % 60)}:${Math.floor((time / 1000) % 60)}:${Math.floor((time / 10) % 100)}`
}, 10)
}
startTimer()
That way, even if the interval value is increased for performance reasons of inactive tabs, the calculation made will guarantee the correct time. This is a vanilla code, but I used this logic in my React application, and you can modify it for wherever you need as well.
It is quite old question but I encountered the same issue.
If you run your web on chrome, you could read through this post Background Tabs in Chrome 57.
Basically the interval timer could run if it haven't run out of the timer budget.
The consumption of budget is based on CPU time usage of the task inside timer.
Based on my scenario, I draw video to canvas and transport to WebRTC.
The webrtc video connection would keep updating even the tab is inactive.
However you have to use setInterval instead of requestAnimationFrame but itt is not recommended for UI rendering though.
It would be better to listen visibilityChange event and change render mechenism accordingly.
Besides, you could try what Kaan Soral suggests and it should works based on the documentation.
I modified Lacerda's response, by adding a functioning UI.
I added start/resume/pause/stop actions.
const
timer = document.querySelector('.timer'),
timerDisplay = timer.querySelector('.timer-display'),
toggleAction = timer.querySelector('[data-action="toggle"]'),
stopAction = timer.querySelector('[data-action="stop"]'),
tickRate = 10;
let intervalId, initialTime, pauseTime = 0;
const now = () => new Date().getTime();
const formatTime = (hours, minutes, seconds) =>
[hours, minutes, seconds]
.map(v => `${isNaN(v) ? 0 : v}`.padStart(2, '0'))
.join(':');
const update = () => {
let
time = (now() - initialTime) + 10,
hours = Math.floor((time / (60000)) % 60),
minutes = Math.floor((time / 1000) % 60),
seconds = Math.floor((time / 10) % 100);
timerDisplay.textContent = formatTime(hours, minutes, seconds);
};
const
startTimer = () => {
initialTime = now();
intervalId = setInterval(update, tickRate);
},
resumeTimer = () => {
initialTime = now() - (pauseTime - initialTime);
intervalId = setInterval(update, tickRate);
},
pauseTimer = () => {
clearInterval(intervalId);
intervalId = null;
pauseTime = now();
},
stopTimer = () => {
clearInterval(intervalId);
intervalId = null;
initialTime = undefined;
pauseTime = 0;
},
restartTimer = () => {
stopTimer();
startTimer();
};
const setButtonState = (button, state, text) => {
button.dataset.state = state;
button.textContent = text;
};
const
handleToggle = (e) => {
switch (e.target.dataset.state) {
case 'pause':
setButtonState(e.target, 'resume', 'Resume');
pauseTimer();
break;
case 'resume':
setButtonState(e.target, 'pause', 'Pause');
resumeTimer();
break;
default:
setButtonState(e.target, 'pause', 'Pause');
restartTimer();
}
},
handleStop = (e) => {
stopTimer();
update();
setButtonState(toggleAction, 'initial', 'Start');
};
toggleAction.addEventListener('click', handleToggle);
stopAction.addEventListener('click', handleStop);
update();
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
background: #000;
}
.timer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5em;
}
.timer .timer-display {
font-family: monospace;
font-size: 3em;
background: #111;
color: #8F8;
border: thin solid #222;
padding: 0.25em;
}
.timer .timer-actions {
display: flex;
justify-content: center;
gap: 0.5em;
}
.timer .timer-actions button[data-action] {
font-family: monospace;
width: 6em;
border: thin solid #444;
background: #222;
color: #EEE;
padding: 0.5em;
cursor: pointer;
text-transform: uppercase;
}
.timer .timer-actions button[data-action]:hover {
background: #444;
border: thin solid #666;
color: #FFF;
}
<div class="timer">
<div class="timer-display"></div>
<div class="timer-actions">
<button data-action="toggle" data-state="initial">Start</button>
<button data-action="stop">Stop</button>
</div>
</div>
Note: This solution is not suitable if you like your interval works on the background, for example, playing audio or something else. But if you are confused for example about your animation not working properly when coming back to your page or tab, this is a good solution.
There are many ways to achieve this goal, maybe the "WebWorkers" is the most standard one but certainly, it's not the easiest and handy one, especially If you don't have enough Time, so you can try this way:
The basic concept:
build a name for your interval(or animation) and set your interval(animation), so it would run when user first time open your page : var interval_id = setInterval(your_func, 3000);
by $(window).focus(function() {}); and $(window).blur(function() {}); you can clearInterval(interval_id) everytime browser(tab) is deactived and ReRun your interval(animation) everytime browser(tab) would acive again by interval_id = setInterval();
Sample code:
var interval_id = setInterval(your_func, 3000);
$(window).focus(function() {
interval_id = setInterval(your_func, 3000);
});
$(window).blur(function() {
clearInterval(interval_id);
interval_id = 0;
});
Here's my rough solution
(function(){
var index = 1;
var intervals = {},
timeouts = {};
function postMessageHandler(e) {
window.postMessage('', "*");
var now = new Date().getTime();
sysFunc._each.call(timeouts, function(ind, obj) {
var targetTime = obj[1];
if (now >= targetTime) {
obj[0]();
delete timeouts[ind];
}
});
sysFunc._each.call(intervals, function(ind, obj) {
var startTime = obj[1];
var func = obj[0];
var ms = obj[2];
if (now >= startTime + ms) {
func();
obj[1] = new Date().getTime();
}
});
}
window.addEventListener("message", postMessageHandler, true);
window.postMessage('', "*");
function _setTimeout(func, ms) {
timeouts[index] = [func, new Date().getTime() + ms];
return index++;
}
function _setInterval(func, ms) {
intervals[index] = [func, new Date().getTime(), ms];
return index++;
}
function _clearInterval(ind) {
if (intervals[ind]) {
delete intervals[ind]
}
}
function _clearTimeout(ind) {
if (timeouts[ind]) {
delete timeouts[ind]
}
}
var intervalIndex = _setInterval(function() {
console.log('every 100ms');
}, 100);
_setTimeout(function() {
console.log('run after 200ms');
}, 200);
_setTimeout(function() {
console.log('closing the one that\'s 100ms');
_clearInterval(intervalIndex)
}, 2000);
window._setTimeout = _setTimeout;
window._setInterval = _setInterval;
window._clearTimeout = _clearTimeout;
window._clearInterval = _clearInterval;
})();
I was able to call my callback function at minimum of 250ms using audio tag and handling its ontimeupdate event. Its called 3-4 times in a second. Its better than one second lagging setTimeout
There is a workaround for this problem, although actually the tab must be made active in some window.
Make your inactive tab a separate browser window.
Don't make any other window maximized (unless the maximized window is behind yours).
This should give the browser the impression of always being active.
This is bit cumbersome, but also a quick win. Provided one has control over windows arrangement.
i am looking for some help with this script i have been working on. here is my file fade.js
the problem is with the changing, it is messed up. please help me find the problem and solution for this, thanks.
JS-file
//image fade script
var opacity = 0;
function changeimg(currentimage){
rnumb = Math.floor(Math.random()*2);
var newimage = "./images/" + rnumb + ".jpg";
if (newimage !== currentimage)
{
document.getElementById("fadeImg").src= newimage;
}
}
function fadein()
{
var fadeImg = document.getElementById('fadeImg');
var browserName=navigator.appName;
if(browserName=="Microsoft Internet Explorer")
{
browserOpacity = opacity / 10;
fadeImg.filters.alpha.opacity = browserOpacity;
}
else
{
browserOpacity = opacity / 1000;
fadeImg.style.opacity = browserOpacity;
}
if(opacity < 1000)
{
initiate();
}
else if(opacity == 1000)
{
changeimg(document.getElementById("fadeImg").src);
opacity = 0;
}
}
function initiate()
{
opacity++;
setInterval("fadein()", 500);
}
index.html
<script type="text/javascript" src="fade.js"></script>
<body onload="initiate()">
<img id="fadeImg" src="images/1.jpg" style="opacity:0.0; filter:alpha(opacity=0)"/>
</body>
JS-fiddle
Here is a Fiddle of the code as well: http://jsfiddle.net/epqKr/2/ (Notice that the code, as it is in the fiddle, may make your browser freeze after a while.
I think you should use a cross-browser library to accomplish things like this.
Microsoft Internet Explorer, especially in versions < 9, is most likely to not behave
as you would expect it does, particularly when you try to use functions which makes use of opacity, alpha-filter and timing. You could try to use jQuery, or Prototype, or MooTools and such frameworks. They all make what you're looking for in simple, secure, better way.
Don't call initiate() from within the fadeIn() function, instead just increment your opacity control variable (i.e, opacity += 1;).
You will probably want to save your setInterval return value to kill the callbacks when you have finished your animation.
You also probably will want to increase the animation speed by lowering the interval.
animId = setInterval("fadeIn()", 5);
i am working on this still, heres what i have now: it doesnt work whatsoever but it looks better.
html
<html>
<head>
<script type="text/javascript" src="fade1.js"></script>
</head>
<body onload="fadetimer()">
<img id="fadeImg" src="1.jpg" style="opacity:0.0; filter:alpha(opacity=0)"/>
</body>
</body>
</html>
script
//image fade script
var opacity = 0;
function changeimg(currentimage){
rnumb = Math.floor(Math.random()*2);
var newimage = rnumb + ".jpg";
if (newimage !== currentimage)
{
document.getElementById("fade").src= newimage;
}
}
function fadein()
{
var fade = document.getElementById('fade');
if(fade.filters.alpha.opacity >= 100 || fade.style.opacity >= 1.0)
{
changeimg(fade.src);
fade.filters.alpha.opacity = 0;
fade.style.opacity = 0;
}
else
{
fade.filters.alpha.opacity += 10;
fade.style.opacity += 0.1;
}
}
function fadetimer()
{
setInterval("fadein()", 500);
}