Page visibility API in Google Chrome - javascript

When using the page visibility API in Google Chrome the event is fired twice.
This is some thing that happen only in Chrome.
document.addEventListener('visibilitychange', function(e) {
if (!document.hidden) {
console.log(e);
}
});
By what I know of the API the event is suppose to fire once.
I am trying to fire it once regardless the browser.

You have to do two different methods to detect browser window and browser tab.
For a cross-browser solution check out this example:
Using HTML5 Visibility API to manage the focus of browser tabs and windows:
For detecting if the browser tab is active or not, use the HTML5 Visibility API:
/////////////////////////////////////////
// main visibility API function
// check if current tab is active or not
var vis = (function(){
var stateKey,
eventKey,
keys = {
hidden: "visibilitychange",
webkitHidden: "webkitvisibilitychange",
mozHidden: "mozvisibilitychange",
msHidden: "msvisibilitychange"
};
for (stateKey in keys) {
if (stateKey in document) {
eventKey = keys[stateKey];
break;
}
}
return function(c) {
if (c) document.addEventListener(eventKey, c);
return !document[stateKey];
}
})();
/////////////////////////////////////////
// check if current tab is active or not
vis(function(){
if(vis()){
// the setTimeout() is used due to a delay
// before the tab gains focus again, very important!
setTimeout(function(){
// browser tab gains focus
// code goes here
},300);
} else {
// browser tab gains focus
// code goes here
}
});
For detecting the browser window you just check the window blur, focus, focusin, and focusout depending on what browser it is:
/////////////////////////////////////////
// check if browser window has focus
var notIE = (document.documentMode === undefined),
isChromium = window.chrome;
if (notIE && !isChromium) {
// checks for Firefox and other NON IE Chrome versions
$(window).on("focusin", function () {
setTimeout(function(){
// browser window gains focus
// code goes here
},300);
}).on("focusout", function () {
// browser window loses focus
// code goes here
});
} else {
// checks for IE and Chromium versions
if (window.addEventListener) {
// bind focus event
window.addEventListener("focus", function (event) {
// the timeout is due to a slight delay when a browser tab regains focus
setTimeout(function(){
// browser window gains focus
// code goes here
},300);
}, false);
// bind blur event
window.addEventListener("blur", function (event) {
// browser window loses focus
// code goes
}, false);
} else {
// bind focus event
window.attachEvent("focus", function (event) {
// the timeout is due to a slight delay when a browser tab regains focus
setTimeout(function(){
// browser window gains focus
// code goes here
},300);
});
// bind focus event
window.attachEvent("blur", function (event) {
// browser window loses focus
// code goes here
});
}
}
Resources:
MDN - visibilitychange
MDN - Using_the_Page_Visibility_API
Working example of the above code: http://codepen.io/jonathan/pen/sxgJl
I also converted this into a jQuery plugin if needed. You can download it on the GreenSock GSAP Forum, at this link here.
Or download the TabWindowVisibilityManager jQuery Plugin directly.
I hope you find this helpful!

Looks like it is a bug in Chrome:
https://code.google.com/p/chromium/issues/detail?id=409467
The workaround seems to be to attach to the window:
Workaround: use window to attach the event to. This seems
non-standard.

Related

Checking Page Visibility and Page Focus for a page

I want to make sure when a user is on the page.
Hence, when a user clicks on another window (looses focus) or changes tab, I should stop playing video on my page.
The problem is trying to do both simultaneously.
For example, through this JS plugin (JQuery Visbility), I am able to check whether the tab/window of my page is open.
Here's how it's doing it:
$(document).on({
'show': function() {
console.log('The page gained visibility; the `show` event was triggered.');
},
'hide': function() {
console.log('The page lost visibility; the `hide` event was triggered.');
}
});
But it can't detect whether the page has focus or not. For example, the page might be open, but I may be opening another window separately and keeping my focus there.
The following code takes care of that (taken from here):
function check()
{
if(document.hasFocus() == lastFocusStatus) return;
lastFocusStatus = !lastFocusStatus;
statusEl.innerText = lastFocusStatus ? 'with' : 'without';
}
window.statusEl = document.getElementById('status');
window.lastFocusStatus = document.hasFocus();
check();
setInterval(check, 200);
Now, I am trying to do both simultaneously. Is it possible?
You can add event listeners for the window's focus and blur events.
var hasFocus = true;
$(window).focus(function(){
hasFocus = true;
});
$(window).blur(function(){
hasFocus = false;
});
//check the hasFocus variable to see if the window has focus

Identifying window focus/blur events with iframes

I'm trying to reliably identify when a browser window/tab is activated and deactivated. Normally, window's focus and blur events would do, but the document contains several iframes.
When an iframe is focused, the main window gets unfocused and vice versa, so we have the following possibilities of focus events [(none) means the window/tab is deactivated]:
current focus new focus events
----------------------------------------------------------------------
window (none) window:blur
window iframe window:blur + iframe:focus
iframe (none) iframe:blur
iframe window iframe:blur + window:focus
iframe another iframe iframe:blur + iframe:focus
(none) window window:focus
(none) iframe iframe:focus
It is no problem to register all of these events, as shown by this fiddle. But whenever we switch from the main window to an iframe or vice versa, or between two iframes, the respective blur and focus events both fire; and they fire with a small delay at that.
I am worried about the concurrency here, since the blur handler could go and start doing stuff, but it should have never started because the user actually just switched focus somewhere in between the frames.
Example: A page should do some AJAX requests periodically whenever it is currently not active. That is, it should start requesting whenever the user deactivates the tab and stop requesting as soon as it's activated again. So we bind a function to the blur event that initiates the requests. If the user just clicks on another iframe, blur, and shortly after that, focus is triggered. But the blur handler already fires away, making at least one request before it can be stopped again.
And that's my problem: How can I reliably detect when a user actually (de-)activates a browser window containing iframes, without risking to get a false alarm caused by two immediate blur and focus events?
I wrote a half-baked solution that uses a timeout after a blur event in order to determine if there was an immediate focus event after it (fiddle):
var active = false,
timeout = 50, // ms
lastBlur = 0,
lastFocus = 0;
function handleBlur() {
if (lastBlur - lastFocus > timeout) {
active = false;
}
}
function handleFocus() {
if (lastFocus - lastBlur > timeout) {
active = true;
}
}
$(window).on('focus', function () {
lastFocus = Date.now();
handleFocus();
}).on('blur', function () {
lastBlur = Date.now();
window.setTimeout(handleBlur, timeout);
});
$('iframe').each(function () {
$(this.contentWindow).on('focus', function () {
lastFocus = Date.now();
handleFocus();
}).on('blur', function () {
lastBlur = Date.now();
window.setTimeout(handleBlur, timeout);
});
});
But I believe this could be very problematic, especially on slower machines. Increasing the timeout is also not acceptable to me, 50 ms is really my pain threshold.
Is there a way that doesn't depend on the client to be fast enough?
you could poll for the document.hasFocus() value, which should be true if either an iframe or the main window are focused
setInterval(function checkFocus(){
if( checkFocus.prev == document.hasFocus() ) return;
if(document.hasFocus()) onFocus();
else onBlur();
checkFocus.prev = document.hasFocus();
},100);
function onFocus(){ console.log('browser window activated') }
function onBlur(){ console.log('browser window deactivated') }
I was trying to do it without polling, but the iframe doesn't fire an onblur event (if the browser window is deactivated when the iframe was on focus, I get no events fired), so I ended up needing polling for half of it anyway, but maybe someone can figure something out with this code
function onFocus(){ console.log('browser window activated'); }
function onBlur(){ console.log('browser window deactivated'); }
var inter;
var iframeFocused;
window.focus(); // I needed this for events to fire afterwards initially
addEventListener('focus', function(e){
console.log('global window focused');
if(iframeFocused){
console.log('iframe lost focus');
iframeFocused = false;
clearInterval(inter);
}
else onFocus();
});
addEventListener('blur', function(e){
console.log('global window lost focus');
if(document.hasFocus()){
console.log('iframe focused');
iframeFocused = true;
inter = setInterval(()=>{
if(!document.hasFocus()){
console.log('iframe lost focus');
iframeFocused = false;
onBlur();
clearInterval(inter);
}
},100);
}
else onBlur();
});

Google Chrome: how to fetch "minimized" event?

I try to fetch all events when the state of a window changes. So far, I use a content script that adds a "resize" listener to the window: window.onresize = function() {...}. This allows me to fetch when a window's state changes to "normal", "maximized" and "fullscreen".
However, I have no idea what to do to also get "minimized". Minimizing a window does not fire "resize" events. I tried to use the onFocusChanged API to add an listener, i.e., chrome.windows.onFocusChanged.addListener(function(windowId) {...}));, but it has its issues. Firstly, if the window I minimize has the focus, windowId = -1 (chrome.windows.WINDOW_ID_NONE), so I cannot fetch the window to readout its state. And secondly, if the window doesn't have the focus, the onFocusChanged event is not fired.
In short, how can I detect when a Chrome window has been minimized?
I think this might help:
chrome.windows.onFocusChanged.addListener(function() {
console.log("Focus changed.");
chrome.windows.getCurrent(function(window){
console.log(window.state);
if(window.state == "normal") {
console.log("It's normal.Stop the watch.");
} else if(window.state == "maximized"){
console.log("It's maximized.Start the watch.");
} else if(window.state == "minimized"){
console.log("It's minimized.Start the watch.");
}
});
});

Cross-browser: detect blur event on window

I just read, I think all the thread that deals with this subject, and I can't find a real solution to my problem.
I need to detect when the browser window loses its focus, i.e. a blur event.
I've tried all the scripts on stackoverflow, but there doesn't seem to be a proper cross-browser approach.
Firefox is the problematic browser here.
A common approach using jQuery is:
window.onblur = function() {
console.log('blur');
}
//Or the jQuery equivalent:
jQuery(window).blur(function(){
console.log('blur');
});
This works in Chrome, IE and Opera, but Firefox doesn't detect the event.
Is there a proper cross-browser way to detect a window blur event?
Or, asked differently, is there a way to detect a window blur event with the Firefox browser?
Related questions and research:
See Firefox 3 window focus and blur
According to the following github articles, jQuery has discontinued support for Firefox blur testing:
https://github.com/jquery/jquery/pull/1423
http://bugs.jquery.com/ticket/13363
I tried both:
document.addEventListener('blur', function(){console.log('blur')});
and
window.addEventListener('blur', function(){console.log('blur')});
and they both worked in my version of FF (33.1).
Here's the jsfiddle: http://jsfiddle.net/hzdd06eh/
Click inside the "run" window and then click outside it to trigger the effect.
The document.hasFocus (MDN) is an implementation that can resolve the problem with Firefox, but in Opera it isn't supported. So, a combined approach can reach out the problem you are facing.
The function below exemplifies how can you use this method:
function getDocumentFocus() {
return document.hasFocus();
}
Since your question isn't clear enough about the application (timed, pub/sub system, event driven, etc), you can use the function above in several ways.
For example, a timed verification can be like the one implemented on this fiddle (JSFiddle).
It appears that jQuery no longer supports these tests for FireFox:
jQuery bug ticket is here: http://bugs.jquery.com/ticket/13363
jQuery close/deprecation commit is here: https://github.com/jquery/jquery/pull/1423
I am searching for a better way to support Firefox blur eventing, but until I find a better approach, this is a more current status relative to the original accepted answer.
You can use jQuery's blur method on window, like so:
$(document).ready(function() {
$(window).blur(function() {
// Put your blur logic here
alert("blur!");
});
});
This works in Firefox, IE, Chrome and Opera.
I tried using the addEventListener DOM function
window.addEventListener('blur', function(){console.log('blur')});
window.addEventListener('click', function(event){console.log(event.clientX)});
I got it to work after the first blur. but it didnt work when I didnt have the click function attached to it.
There might be some kind of refresh that happens when a click function is interpreted
Here is an alternative solution to your question but it uses the Page Visibility API and Solution is Cross Browser compatible.
(function() {
var hidden = "hidden";
// Standards:
if (hidden in document)
document.addEventListener("visibilitychange", onchange);
else if ((hidden = "mozHidden") in document)
document.addEventListener("mozvisibilitychange", onchange);
else if ((hidden = "webkitHidden") in document)
document.addEventListener("webkitvisibilitychange", onchange);
else if ((hidden = "msHidden") in document)
document.addEventListener("msvisibilitychange", onchange);
// IE 9 and lower:
else if ("onfocusin" in document)
document.onfocusin = document.onfocusout = onchange;
// All others:
else
window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange;
function onchange(evt) {
var v = "visible",
h = "hidden",
evtMap = {
focus: v,
focusin: v,
pageshow: v,
blur: h,
focusout: h,
pagehide: h
};
evt = evt || window.event;
if (evt.type in evtMap) {
console.log(evtMap[evt.type]);
} else {
console.log(this[hidden] ? "hidden" : "visible");
}
}
// set the initial state (but only if browser supports the Page Visibility API)
if (document[hidden] !== undefined)
onchange({
type: document[hidden] ? "blur" : "focus"
});
})();

Closing a pop up window when it loses focus

I am wondering if it is possible to create a pop up window with javascript, and then close that window when it loses focus.
Here is my code:
var theWindow;
function launchWindow() {
theWindow=window.open('www.domain.com');
theWindow.onblur = function() {
this.close();
};
}
But that doesn't work at all. Any suggestions?
EDIT: I have discovered a solution that works for me, hopefully it will work for someone else:
var theWindow;
var windows = [];
function launchWindow() {
theWindow=window.open('www.domain.com');
windows.push(theWindow);
window.onfocus = function() {
for (x in windows) {
windows[x].close();
}
};
}
It's not an exact solution to my original problem (It doesn't close the window when it loses focus, but rather it closes it when the main window regains focus) but it works for me.
Is the URL of the popup window from the same domain as the parent? If not, you will likely not be able to attach an event to the new window's onblur event.
Run this from your browser console while viewing StackOverflow to see that it does in fact work when the popup is on the same domain as the originating window:
var theWindow = window.open ("http://www.stackoverflow.com","theWindow");
theWindow.onblur = function() { this.close(); };
window does not have the onblur event
Try to call it's closing by focusing on the <body> of the main window
The problem you may be having is that you are binding the onblur handler before the window has begun loading the page. Once the page is loaded, your onblur handler is gone. You'll need to defer binding long enough to give the page a chance to start loading before binding your event handler:
function launchWindow() {
var theWindow = window.open('www.domain.com');
setTimeout(function() {
theWindow.onblur = function() {
this.close();
};
}, 2000);
}
If you are loading a page in a different domain, you won't be able to bind the onblur handler at all. You'll need to stick to your solution using onfocus.

Categories

Resources