我有JavaScript,它周期性地执行活动。当用户没有在看站点时(例如,窗口或选项卡没有焦点),最好不要运行。
是否有一种方法可以使用JavaScript来做到这一点?
我的参考点:如果你正在使用的窗口不活跃,Gmail聊天就会播放声音。
我有JavaScript,它周期性地执行活动。当用户没有在看站点时(例如,窗口或选项卡没有焦点),最好不要运行。
是否有一种方法可以使用JavaScript来做到这一点?
我的参考点:如果你正在使用的窗口不活跃,Gmail聊天就会播放声音。
当前回答
我开始使用社区维基答案,但意识到它没有检测alt-tab事件在Chrome。这是因为它使用了第一个可用的事件源,在这种情况下,它是页面可见性API,在Chrome中似乎不跟踪alt-tab。
我决定稍微修改一下脚本,以跟踪页面焦点更改的所有可能事件。这是一个你可以放入的函数:
function onVisibilityChange(callback) {
var visible = true;
if (!callback) {
throw new Error('no callback given');
}
function focused() {
if (!visible) {
callback(visible = true);
}
}
function unfocused() {
if (visible) {
callback(visible = false);
}
}
// Standards:
if ('hidden' in document) {
visible = !document.hidden;
document.addEventListener('visibilitychange',
function() {(document.hidden ? unfocused : focused)()});
}
if ('mozHidden' in document) {
visible = !document.mozHidden;
document.addEventListener('mozvisibilitychange',
function() {(document.mozHidden ? unfocused : focused)()});
}
if ('webkitHidden' in document) {
visible = !document.webkitHidden;
document.addEventListener('webkitvisibilitychange',
function() {(document.webkitHidden ? unfocused : focused)()});
}
if ('msHidden' in document) {
visible = !document.msHidden;
document.addEventListener('msvisibilitychange',
function() {(document.msHidden ? unfocused : focused)()});
}
// IE 9 and lower:
if ('onfocusin' in document) {
document.onfocusin = focused;
document.onfocusout = unfocused;
}
// All others:
window.onpageshow = window.onfocus = focused;
window.onpagehide = window.onblur = unfocused;
};
像这样使用它:
onVisibilityChange(function(visible) {
console.log('the page is now', visible ? 'focused' : 'unfocused');
});
该版本监听所有不同的可见性事件,并在其中任何事件导致更改时触发回调。焦点处理程序和非焦点处理程序确保如果多个api捕捉到相同的可见性更改,不会多次调用回调。
其他回答
简单的/立即检查:
if(document.hidden) {
// do something
}
可见性变化事件:
document.addEventListener("visibilitychange", function() {
console.log(document.visibilityState); // "hidden" or "visible"
}, false);
基于承诺的事件:
// An `await`able function that resolves when page visibility changes:
function visibilityChange(state="") {
return new Promise(resolve => {
document.addEventListener("visibilitychange", function() {
if(!state || document.visibilityState === state) {
resolve(document.visibilityState);
document.removeEventListener("visibilitychange", arguments.callee);
}
});
});
}
// Use it like this:
await visibilityChange();
console.log(document.visibilityState);
// Or wait for page to become...
await visibilityChange("visible");
await visibilityChange("hidden");
(注:后两个解是我加到这个答案里的,因为这个问题现在已经结束了,我不能加我自己的答案。以防有人认为我抄袭了那篇文章而没有注明出处。)
我重读了@daniel-buckmaster的版本 我没有做多次尝试,但是,代码对我来说似乎更优雅…
// on-visibility-change.js v1.0.1, based on https://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active#38710376
function onVisibilityChange(callback) {
let d = document;
let visible = true;
let prefix;
if ('hidden' in d) {
prefix = 'h';
} else if ('webkitHidden' in d) {
prefix = 'webkitH';
} else if ('mozHidden' in d) {
prefix = 'mozH';
} else if ('msHidden' in d) {
prefix = 'msH';
} else if ('onfocusin' in d) { // ie 9 and lower
d.onfocusin = focused;
d.onfocusout = unfocused;
} else { // others
window.onpageshow = window.onfocus = focused;
window.onpagehide = window.onblur = unfocused;
};
if (prefix) {
visible = !d[prefix + 'idden'];
d.addEventListener(prefix.substring(0, prefix.length - 1) + 'visibilitychange', function() {
(d[prefix + 'idden'] ? unfocused : focused)();
});
};
function focused() {
if (!visible) {
callback(visible = true);
};
};
function unfocused() {
if (visible) {
callback(visible = false);
};
};
};
这是根据安迪?
这将执行一个任务,例如每30秒刷新一次页面, 但前提是页面是可见的和集中的。
如果无法检测到可见性,则只使用焦点。
如果用户聚焦页面,那么它将立即更新
页面直到ajax调用后30秒才会再次更新
var windowFocused = true;
var timeOut2 = null;
$(function(){
$.ajaxSetup ({
cache: false
});
$("#content").ajaxComplete(function(event,request, settings){
set_refresh_page(); // ajax call has just been made, so page doesn't need updating again for 30 seconds
});
// check visibility and focus of window, so as not to keep updating unnecessarily
(function() {
var hidden, change, vis = {
hidden: "visibilitychange",
mozHidden: "mozvisibilitychange",
webkitHidden: "webkitvisibilitychange",
msHidden: "msvisibilitychange",
oHidden: "ovisibilitychange" /* not currently supported */
};
for (hidden in vis) {
if (vis.hasOwnProperty(hidden) && hidden in document) {
change = vis[hidden];
break;
}
}
document.body.className="visible";
if (change){ // this will check the tab visibility instead of window focus
document.addEventListener(change, onchange,false);
}
if(navigator.appName == "Microsoft Internet Explorer")
window.onfocus = document.onfocusin = document.onfocusout = onchangeFocus
else
window.onfocus = window.onblur = onchangeFocus;
function onchangeFocus(evt){
evt = evt || window.event;
if (evt.type == "focus" || evt.type == "focusin"){
windowFocused=true;
}
else if (evt.type == "blur" || evt.type == "focusout"){
windowFocused=false;
}
if (evt.type == "focus"){
update_page(); // only update using window.onfocus, because document.onfocusin can trigger on every click
}
}
function onchange () {
document.body.className = this[hidden] ? "hidden" : "visible";
update_page();
}
function update_page(){
if(windowFocused&&(document.body.className=="visible")){
set_refresh_page(1000);
}
}
})();
set_refresh_page();
})
function get_date_time_string(){
var d = new Date();
var dT = [];
dT.push(d.getDate());
dT.push(d.getMonth())
dT.push(d.getFullYear());
dT.push(d.getHours());
dT.push(d.getMinutes());
dT.push(d.getSeconds());
dT.push(d.getMilliseconds());
return dT.join('_');
}
function do_refresh_page(){
// do tasks here
// e.g. some ajax call to update part of the page.
// (date time parameter will probably force the server not to cache)
// $.ajax({
// type: "POST",
// url: "someUrl.php",
// data: "t=" + get_date_time_string()+"&task=update",
// success: function(html){
// $('#content').html(html);
// }
// });
}
function set_refresh_page(interval){
interval = typeof interval !== 'undefined' ? interval : 30000; // default time = 30 seconds
if(timeOut2 != null) clearTimeout(timeOut2);
timeOut2 = setTimeout(function(){
if((document.body.className=="visible")&&windowFocused){
do_refresh_page();
}
set_refresh_page();
}, interval);
}
有3种典型的方法来确定用户是否可以看到HTML页面,但是没有一种方法是完美的:
The W3C Page Visibility API is supposed to do this (supported since: Firefox 10, MSIE 10, Chrome 13). However, this API only raises events when the browser tab is fully overriden (e.g. when the user changes from one tab to another one). The API does not raise events when the visibility cannot be determined with 100% accuracy (e.g. Alt+Tab to switch to another application). Using focus/blur based methods gives you a lot of false positive. For example, if the user displays a smaller window on top of the browser window, the browser window will lose the focus (onblur raised) but the user is still able to see it (so it still need to be refreshed). See also http://javascript.info/tutorial/focus Relying on user activity (mouse move, clicks, key typed) gives you a lot of false positive too. Think about the same case as above, or a user watching a video.
为了改善上面描述的不完美行为,我使用了三种方法的组合:W3C可视性API,然后是焦点/模糊和用户活动方法,以降低假阳性率。这允许管理以下事件:
将浏览器选项卡更改为另一个选项卡(100%的准确性,感谢W3C页面可见性API) 页面可能被另一个窗口隐藏,例如,由于Alt+Tab(概率=不是100%准确) 用户的注意力可能不会集中在HTML页面上(概率=不是100%准确)
This is how it works: when the document lose the focus, the user activity (such as mouse move) on the document is monitored in order to determine if the window is visible or not. The page visibility probability is inversely proportional to the time of the last user activity on the page: if the user makes no activity on the document for a long time, the page is most probably not visible. The code below mimics the W3C Page Visibility API: it behaves the same way but has a small false positive rate. It has the advantage to be multibrowser (tested on Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).
<div id="x"></div>
<script>
/**
Registers the handler to the event for the given object.
@param obj the object which will raise the event
@param evType the event type: click, keypress, mouseover, ...
@param fn the event handler function
@param isCapturing set the event mode (true = capturing event, false = bubbling event)
@return true if the event handler has been attached correctly
*/
function addEvent(obj, evType, fn, isCapturing){
if (isCapturing==null) isCapturing=false;
if (obj.addEventListener){
// Firefox
obj.addEventListener(evType, fn, isCapturing);
return true;
} else if (obj.attachEvent){
// MSIE
var r = obj.attachEvent('on'+evType, fn);
return r;
} else {
return false;
}
}
// register to the potential page visibility change
addEvent(document, "potentialvisilitychange", function(event) {
document.getElementById("x").innerHTML+="potentialVisilityChange: potentialHidden="+document.potentialHidden+", document.potentiallyHiddenSince="+document.potentiallyHiddenSince+" s<br>";
});
// register to the W3C Page Visibility API
var hidden=null;
var visibilityChange=null;
if (typeof document.mozHidden !== "undefined") {
hidden="mozHidden";
visibilityChange="mozvisibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden="msHidden";
visibilityChange="msvisibilitychange";
} else if (typeof document.webkitHidden!=="undefined") {
hidden="webkitHidden";
visibilityChange="webkitvisibilitychange";
} else if (typeof document.hidden !=="hidden") {
hidden="hidden";
visibilityChange="visibilitychange";
}
if (hidden!=null && visibilityChange!=null) {
addEvent(document, visibilityChange, function(event) {
document.getElementById("x").innerHTML+=visibilityChange+": "+hidden+"="+document[hidden]+"<br>";
});
}
var potentialPageVisibility = {
pageVisibilityChangeThreshold:3*3600, // in seconds
init:function() {
function setAsNotHidden() {
var dispatchEventRequired=document.potentialHidden;
document.potentialHidden=false;
document.potentiallyHiddenSince=0;
if (dispatchEventRequired) dispatchPageVisibilityChangeEvent();
}
function initPotentiallyHiddenDetection() {
if (!hasFocusLocal) {
// the window does not has the focus => check for user activity in the window
lastActionDate=new Date();
if (timeoutHandler!=null) {
clearTimeout(timeoutHandler);
}
timeoutHandler = setTimeout(checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold*1000+100); // +100 ms to avoid rounding issues under Firefox
}
}
function dispatchPageVisibilityChangeEvent() {
unifiedVisilityChangeEventDispatchAllowed=false;
var evt = document.createEvent("Event");
evt.initEvent("potentialvisilitychange", true, true);
document.dispatchEvent(evt);
}
function checkPageVisibility() {
var potentialHiddenDuration=(hasFocusLocal || lastActionDate==null?0:Math.floor((new Date().getTime()-lastActionDate.getTime())/1000));
document.potentiallyHiddenSince=potentialHiddenDuration;
if (potentialHiddenDuration>=potentialPageVisibility.pageVisibilityChangeThreshold && !document.potentialHidden) {
// page visibility change threshold raiched => raise the even
document.potentialHidden=true;
dispatchPageVisibilityChangeEvent();
}
}
var lastActionDate=null;
var hasFocusLocal=true;
var hasMouseOver=true;
document.potentialHidden=false;
document.potentiallyHiddenSince=0;
var timeoutHandler = null;
addEvent(document, "pageshow", function(event) {
document.getElementById("x").innerHTML+="pageshow/doc:<br>";
});
addEvent(document, "pagehide", function(event) {
document.getElementById("x").innerHTML+="pagehide/doc:<br>";
});
addEvent(window, "pageshow", function(event) {
document.getElementById("x").innerHTML+="pageshow/win:<br>"; // raised when the page first shows
});
addEvent(window, "pagehide", function(event) {
document.getElementById("x").innerHTML+="pagehide/win:<br>"; // not raised
});
addEvent(document, "mousemove", function(event) {
lastActionDate=new Date();
});
addEvent(document, "mouseover", function(event) {
hasMouseOver=true;
setAsNotHidden();
});
addEvent(document, "mouseout", function(event) {
hasMouseOver=false;
initPotentiallyHiddenDetection();
});
addEvent(window, "blur", function(event) {
hasFocusLocal=false;
initPotentiallyHiddenDetection();
});
addEvent(window, "focus", function(event) {
hasFocusLocal=true;
setAsNotHidden();
});
setAsNotHidden();
}
}
potentialPageVisibility.pageVisibilityChangeThreshold=4; // 4 seconds for testing
potentialPageVisibility.init();
</script>
由于目前还没有有效的跨浏览器解决方案,所以在禁用网站上的周期性活动时最好三思而后行。
对于没有jQuery的解决方案,请参阅Visibility.js,它提供了关于三个页面状态的信息
visible ... page is visible
hidden ... page is not visible
prerender ... page is being prerendered by the browser
以及setInterval的方便包装器
/* Perform action every second if visible */
Visibility.every(1000, function () {
action();
});
/* Perform action every second if visible, every 60 sec if not visible */
Visibility.every(1000, 60*1000, function () {
action();
});
旧版浏览器(IE < 10;iOS < 7)也可用