How to tell if browser/tab is active [duplicate] - javascript

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is there a way to detect if a browser window is not currently active?
I have a function that is called every second that I only want to run if the current page is in the foreground, i.e. the user hasn't minimized the browser or switched to another tab. It serves no purpose if the user isn't looking at it and is potentially CPU-intensive, so I don't want to just waste cycles in the background.
Does anyone know how to tell this in JavaScript?
Note: I use jQuery, so if your answer uses that, that's fine :).

In addition to Richard Simões answer you can also use the Page Visibility API.
if (!document.hidden) {
// do what you need
}
This specification defines a means for site developers to
programmatically determine the current visibility state of the page in
order to develop power and CPU efficient web applications.
Learn more (2019 update)
All modern browsers are supporting document.hidden
http://davidwalsh.name/page-visibility
https://developers.google.com/chrome/whitepapers/pagevisibility
Example pausing a video when window/tab is hidden https://web.archive.org/web/20170609212707/http://www.samdutton.com/pageVisibility/

You would use the focus and blur events of the window:
var interval_id;
$(window).focus(function() {
if (!interval_id)
interval_id = setInterval(hard_work, 1000);
});
$(window).blur(function() {
clearInterval(interval_id);
interval_id = 0;
});
To Answer the Commented Issue of "Double Fire" and stay within jQuery ease of use:
$(window).on("blur focus", function(e) {
var prevType = $(this).data("prevType");
if (prevType != e.type) { // reduce double fire issues
switch (e.type) {
case "blur":
// do work
break;
case "focus":
// do work
break;
}
}
$(this).data("prevType", e.type);
})
Click to view Example Code Showing it working (JSFiddle)

I would try to set a flag on the window.onfocus and window.onblur events.
The following snippet has been tested on Firefox, Safari and Chrome, open the console and move between tabs back and forth:
var isTabActive;
window.onfocus = function () {
isTabActive = true;
};
window.onblur = function () {
isTabActive = false;
};
// test
setInterval(function () {
console.log(window.isTabActive ? 'active' : 'inactive');
}, 1000);
Try it out here.

Using jQuery:
$(function() {
window.isActive = true;
$(window).focus(function() { this.isActive = true; });
$(window).blur(function() { this.isActive = false; });
showIsActive();
});
function showIsActive()
{
console.log(window.isActive)
window.setTimeout("showIsActive()", 2000);
}
function doWork()
{
if (window.isActive) { /* do CPU-intensive stuff */}
}

All of the examples here (with the exception of rockacola's) require that the user physically click on the window to define focus. This isn't ideal, so .hover() is the better choice:
$(window).hover(function(event) {
if (event.fromElement) {
console.log("inactive");
} else {
console.log("active");
}
});
This'll tell you when the user has their mouse on the screen, though it still won't tell you if it's in the foreground with the user's mouse elsewhere.

If you are trying to do something similar to the Google search page when open in Chrome, (where certain events are triggered when you 'focus' on the page), then the hover() event may help.
$(window).hover(function() {
// code here...
});

Related

Disable Keyboard Shortcuts on Webpage

I am working with some others on a web based javascript game, and there is a small issue where occasionally keyboard shortcuts can get in the way. sometimes for example, one might accidentally press Alt+W and that can be annoying (and sometimes a little scary depending on what your doing).
So i would like to know, is there any way to use javascript to disable shortcuts on a webpage? i have looked around quite a lot and have not found anything about how to do this
you need to use key codes. try with something like this:
window.onload = function() {
window.document.body.onkeydown = function() {
if (event.ctrlKey) {
event.stopPropagation();
event.preventDefault();
try {
event.keyCode = 0;
}
catch (event) {
}
return false;
}
return true;
}
}

jQuery animation function breaks when switching tabs

UPDATE: I reproduced the error in a plunkr: See http://plnkr.co/BpYfCNBESUT6ZkiSZHgx
The problem occurs when you do following:
Open website. Refresh website, while you see the page is loading in the browser tab, when you see the spinner in the tab.
Switch to another tab in your browser. If it doens't happen. Try again. If did as said, you are most likely seeing this:
You might say, this is not such a big deal, you have to be real fast to let this error happen. However, imagine somebody with a slow connection, he goes to another tab in his browser, continue watching his youtube video, while website is loading. When he comes back he just sees the page keeps loading.
Lets say ive this animation code:
$pageloaderbar.animate({
width: "46%"
}, {
duration: 700,
complete: function () {
$scope.continue();
}
});
When the first animation completes it calls an new function, called $scope.continue();. Which looks likes this:
$scope.continue = function () {
$timeout(function () {
$pageloaderbar.animate({
width: "100%"
}, {
duration: 500,
complete: function () {
$scope.PageIsLoading = false;
}
});
});
}
The problem is, when a user switches tab in his browser, between the $pageloaderbar.animate and the $scope.continue, the $plageloaderbar.animate function never reaches the complete function. The browser console shows the following error (Chrome)
My question is, how can i see if the user is active on the website? Or, how can i still execute the function, even if the user is not active on the browser tab?
Because there seems no one with an awnser, i have figured an little workaround myself. However, if someone still can explain why the animation breaks when switching tab, im very pleased.
The workaround was quite simple, i only had to add this code.
complete: function () {
if(document.hidden) {
$(window).on("blur focus", function () {
$scope.continue();
});
} else {
$scope.continue();
}
}
instead of:
complete: function () {
$scope.continue();
}
This is being caused by the jQuery animate function, which passes the animate options object to a function called speed. This function checks to see if the document is hidden - which it will be if the tab is inactive. If it is hidden, (or fx.off is set to true), all animation durations are set to 0.
You can see this on line 7137 in your plunkr's jQuery file.
if ( jQuery.fx.off || document.hidden ) {
opt.duration = 0;
As the animation now has no duration, it becomes synchronous, and so the fact that your complete function comes after the call to animate, is an issue.
To fix this, you would need to move the complete function declaration above your call to animate.
In the newer version of chrome this is related to visibility of the tab and your function is breaking due to that. you can use visibility API to know that tab is visible to the user or not.
Please have a look at this link which i found :
http://dystroy.org/demos/vis-en.html
$scope.continue = function () {
$timeout(function () {
$pageloaderbar.animate({
width: "100%"
}, {
duration: 500,
complete: function () {
$scope.PageIsLoading = false;
$("body").css("overflow", "auto");
$pageloaderwrap.addClass("page-loader-finished");
$scope.pageReady();
}
});
});
}
var $pageloaderwrap = $(".page-loader-wrap");
var $pageloader = $(".page-loader");
var $pageloaderbar = $(".page-loader .page-loader-bar");
$("body").css("overflow", "hidden");
$pageloaderbar.animate({
width: "46%"
}, {
duration: 700,
complete: function () {
if (document.hidden) {
$(window).on("blur focus", function () {
$scope.continue();
});
} else {
$scope.continue();
}
}
});
Try to define your continue function before call $pageloaderbar.animate . That may not be the main problem but, it is always possible to have a issue because of that.
Your solution is just a patch, not a real solution and not seems good. Even if it solves your problem, you must find a certain way to prevent this error.
I strongly recommend you to leave jquery. Such problems usually comes from jquery - angular incompatibilities.

Checking for unsaved changes when switching views in backbone.js

I am new to Javascript and backbone.js, so hopefully I am missing something simple here. I am experimenting with some sample code I found which is supposed to check for unsaved changes before allowing a user to navigate away to another page. I have created a JSfiddle here:
http://jsfiddle.net/U43T5/4/
The code subscribes to the hashchange event like this:
$(window).on("hashchange", router.hashChange);
And the router.hashChange function checks a "dirty" flag to determine whether or not to allow the navigation, like this:
hashChange: function (evt) {
if (this.cancelNavigate) { // cancel out if just reverting the URL
evt.stopImmediatePropagation();
this.cancelNavigate = false;
return;
}
if (this.view && this.view.dirty) {
var dialog = confirm("You have unsaved changes. To stay on the page, press cancel. To discard changes and leave the page, press OK");
if (dialog == true) return;
else {
evt.stopImmediatePropagation();
this.cancelNavigate = true;
window.location.href = evt.originalEvent.oldURL;
}
}
},
The problem is that the code is not working because this.view is undefined, so the 2nd if block is never entered.
I would like the sample program to always ask for confirmation before navigating away from the page (in my sample program, I have set this.view.dirty to always be true, which is why it should always ask for confirmation). Or if there is a better approach, I am open to alternatives.
The main issue is the this context in the methods , this corresponds to the Window Object and not the Router. So it always remains undefined as you are defining view on the router. Declare a initialize method which binds the context inside the methods to router.
initialize: function() {
_.bindAll(this, 'loadView', 'hashChange');
},
Check Fiddle
I spent a lot of time to make at least something decent.
I ended up writing a wrapper for Backbone function:
var ignore = false;
Backbone.history.checkUrl = function() {
if (ignore) {
ignore = false;
return;
}
app.dirtyModelHandler.confirm(this, function () {
Backbone.History.prototype.checkUrl.call(Backbone.history);
},
function() {
ignore = true;
window.history.forward();
});
};
app.dirtyModelHandler.confirm is a function which shows confirmation (Ok, Cancel) view and takes success and cancel functions as arguments.

Separate desktop and mobile jQuery functions on a responsive site

I'm working on a responsive site with a specific set of jQuery functions for the desktop layout and mobile layout. They interfere with each other if they're both active at the same time.
By checking window.width, I'm able to deliver only the correct set of functions on page load, and I'd like to do the same on window.resize.
I've set up a stripped down Fiddle of where I'm at here: http://jsfiddle.net/b9XEj/
Two problems exist right now:
Either desktopFunctions or mobileFunctions will continuously fire on page resize, whether they have already been loaded or not.
If the window is resized beyond one breakpoint and then returned to the previous size, the incorrect set of functions will already have been loaded, interfering with the current set.
The window.resize function should behave in the following way:
Check if the correct set of functions currently active for the viewport size
If yes, return.
If no, fire correct set of functions and remove incorrect set of functions if they exist.
In the Fiddle example above, you would always see a single line, displaying either "Mobile Functions are active" or "Desktop Functions are active".
I'm a bit lost at this point, but I have tried using
if ($.isFunction(window.mobileFunctions))
to check if functions already exist, but I can't seem to get it working without breaking the overall function. Here's a fiddle for that code: http://jsfiddle.net/nA8TB/
Thinking ahead, this attempt also wouldn't take into account whether the incorrect set of functions exists already. So, I'm really hoping there's a way I can deal with this in a simpler way and solve both problems.
Any help would be much appreciated!
Following conquers 2 of the problems. The resize fires many times a second, so using a timeout will fix it firing your code constantly. It also adds a check to see if the same size is in effect, and return if it is
$(document).ready(function() {
var windowType;
var $wind = $(window);
var desktopFunctions = function() {
$('body').append('<p>Desktop functions are active</p>');
}
var mobileFunctions = function() {
$('body').append('<p>Mobile Functions are active</p>');
}
var mobileCheck = function() {
var window_w = $wind.width();
var currType = window_w < 940 ? 'mobile' :'desktop';
if (windowType == currType) {
$('body').append('<p>No Type Change, Width= '+window_w+'</p>');
return;
} else {
windowType = currType;
}
if (windowType == 'mobile') {
mobileFunctions();
} else {
desktopFunctions();
}
}
mobileCheck();
var resizeTimer;
$wind.resize(function() {
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(mobileCheck, 300)
});
});
DEMO: http://jsfiddle.net/b9XEj/1/
Without seeing some real world differences between your 2 sets of functions it is hard to provide gudance on how to stop them conflicting. One possibility is checking the windowType in your functions
You can prevent the continuous firing by adding a delay mobileCheck. Use a setTimeout along with a checkPending boolean value.
var checkPending = false;
$(window).resize(function(){
if (checkPending === false) {
checkPending = true;
setTimeout(mobileCheck, 1000);
}
});
See here: http://jsfiddle.net/2Q3pT/
Edit
As far as the second requirement, you could use this pattern to create or use the existing one:
mobileFunctions = mobileFunctions || function() {
// mobile functions active
};
See: http://jsfiddle.net/2Q3pT/2/

When using setInterval, if I switch tabs in Chrome and go back, the slider goes crazy catching up

I have a jQuery slider on my site and the code going to the next slide is in a function called nextImage. I used setInterval to run my function on a timer, and it does exactly what I want: it runs my slides on a timer. BUT, if I go to the site in Chrome, switch to another tab and return, the slider runs through the slides continuously until it 'catches up'. Does anyone know of a way to fix this. The following is my code.
setInterval(function() {
nextImage();
}, 8000);
How to detect when a tab is focused or not in Chrome with Javascript?
window.addEventListener('focus', function() {
document.title = 'focused';
},false);
window.addEventListener('blur', function() {
document.title = 'not focused';
},false);
To apply to your situation:
var autopager;
function startAutopager() {
autopager = window.setInterval(nextImage, 8000);
}
function stopAutopager() {
window.clearInterval(autopager);
}
window.addEventListener('focus', startAutopager);
window.addEventListener('blur', stopAutopager);
Note that in the latest version of Chromium, there is either a bug or a 'feature' which is making this less reliable, requiring that the user has clicked at least once anywhere in the window. See linked question above for details.
I post an answer here: How can I make setInterval also work when a tab is inactive in Chrome?
Just do this:
setInterval(function() {
$("#your-image-container").stop(true,true);
nextImage();
}, 1000);
inactive browser tabs buffer some of the setInterval or setTimeout functions.
stop(true,true) - will stop all buffered events and execute immadietly only 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).
A few ideas comes to mind:
Idea #1
You can make it so that a short burst is idempotent. For example, you could say:
function now() {
return (new Date()).getTime();
}
var autopagerInterval = 8000;
function startAutopager() {
var startImage = getCurrentImageNumber();
var startTime = now();
var autopager = setInterval(
function() {
var timeSinceStart = now() - startTime();
var targetImage = getCurrentImageNumber + Math.ceil(timeSinceStart/autopagerInterval);
if (getCurrentImageNumber() != targetImage)
setImageNumber(targetImage); // trigger animation, etc.
},
autopagerInterval
);
return autopager;
}
This way even if the function runs 1000 times, it will still run in only a few milliseconds and animate only once.
note: If the user leaves the page and comes back, it will have scrolled. This is probably not what the original poster wants, but I leave this solution up since it is sometimes what you want.
Idea #2
Another way to add idempotence (while still keeping your nextImage() function and not having it scroll to the bottom of the page) would be to have the function set a mutex lock which disappears after a second (cleared by another timeout). Thus even if the setInterval function was called 1000 times, only the first instance would run and the others would do nothing.
var locked = false;
var autopager = window.setInterval(function(){
if (!locked) {
locked = true;
window.setTimeout(function(){
locked=false;
}, 1000);
nextImage();
}
}, 8000);
edit: this may not work, see below
Idea #3
I tried the following test:
function f() {
console.log((new Date()) + window.focus());
window.setTimeout(f, 1000);
}
f();
It seems to indicate that the function is being called every second. This is odd... but I think this means that the callbacks are being called, but that the page renderer refuses to update the page in any graphical way while the tab is unfocused, delaying all operations until the user returns, but operations keep piling up.
Also the window.focus() function doesn't say if the window has focus; it GIVES focus to the window, and is thus irrelevant.
What we want is probably this: How to detect when a tab is focused or not in Chrome with Javascript? -- you can unset your interval when the window loses focus (blur), and reset it when it gains focus.
I don't know exactly what is going on in your function nextImage(), but I had a similar issue. I was using animate() with setInterval() on a jQuery image slider that I created, and I was experiencing the same thing as you when I switched to a different tab and back again. In my case the animate() function was being queued, so once the window regained focus the slider would go crazy. To fix this I just stopped the animate() function from queuing.
There are a couple ways you can do this. the easiest is with .stop(), but this issue and ways to fix it are documented in the jQuery docs. Check this page near the bottom under the heading additional notes: http://api.jquery.com/animate/
I had faced similar issue, somehow this code below works fine for me.
var t1= window.setInterval('autoScroll()', 8000);
window.addEventListener('focus', function() {
focused = true;
window.clearInterval(t1);
t1 = window.setInterval('autoScroll()', 8000);
},false);
window.addEventListener('blur', function() {
focused = false;
window.clearInterval(t1);
},false)
function autoScroll()
{
if ( running == true){
if ( focused = true){
forwardSlide();
}
}
else {
running = true;
}
}
If you are using Soh Tanaka's image slider then just add this...to solve your Google Chrome issue:
$(".image_reel").stop(true, true).fadeOut(300).animate({ left: -image_reelPosition}, 500 ).fadeIn(300);
Take note of the .stop() function. Ignore the fading in and out stuff, that's what I used on my version
Thanks
Seconding the comment by jgerstle to use page visibility events instead, see https://www.w3.org/TR/page-visibility/#example-1-visibility-aware-video-playback for more around subscribing to 'visibilitychange' for hidden/visible states.
This seems to be more useful than focus/blur these days as it covers visible-but-not-selected windows if concerned also about multi-window operating systems.

Categories

Resources