我有定期执行活动的JavaScript。当用户没有看到该网站时(即窗口或标签没有焦点),最好不要运行。
有没有办法使用JavaScript?
我的参考点:如果您使用的窗口未处于活动状态,则Gmail聊天会播放声音。
答案 0 :(得分:633)
由于最初编写此答案,由于W3C,新规范已达到推荐状态。 Page Visibility API(MDN)现在允许我们更准确地检测用户何时隐藏页面。
当前的浏览器支持:
以下代码使用API,在不兼容的浏览器中回退到不太可靠的模糊/焦点方法。
(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)
document.body.className = evtMap[evt.type];
else
document.body.className = 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"});
})();
onfocusin
和onfocusout
为required for IE 9 and lower,而所有其他人都使用onfocus
和onblur
,但使用onpageshow
的iOS除外和onpagehide
。
答案 1 :(得分:124)
我会使用jQuery,因为你要做的就是:
$(window).blur(function(){
//your code here
});
$(window).focus(function(){
//your code
});
或者至少它对我有用。
答案 2 :(得分:44)
有3种典型方法用于确定用户是否可以看到HTML页面,但是它们都不能完美运行:
W3C Page Visibility API应该这样做(支持自:Firefox 10,MSIE 10,Chrome 13)。但是,此API仅在完全覆盖浏览器选项卡时(例如,当用户从一个选项卡更改为另一个选项卡时)引发事件。如果无法以100%的准确度确定可见性,则API不会引发事件(例如,Alt + Tab切换到另一个应用程序)。
使用基于焦点/模糊的方法会给您带来很多误报。例如,如果用户在浏览器窗口顶部显示较小的窗口,则浏览器窗口将失去焦点(onblur
已提升),但用户仍然可以看到它(因此仍需要刷新) 。另请参阅http://javascript.info/tutorial/focus
为了改善上述不完美行为,我结合使用了3种方法:W3C Visibility API,然后是焦点/模糊和用户活动方法,以降低误报率。这允许管理以下事件:
这就是它的工作原理:当文档松开焦点时,会监视文档上的用户活动(例如鼠标移动),以确定窗口是否可见。页面可见性概率与页面上最后一次用户活动的时间成反比:如果用户长时间未对文档进行任何活动,则该页面很可能不可见。下面的代码模仿W3C页面可见性API:它的行为方式相同,但误报率很低。它具有多浏览器的优势(在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>
由于目前没有可用的跨浏览器解决方案而且没有误报,因此您最好三思而后行,禁用网站上的定期活动。
答案 3 :(得分:23)
GitHub上有一个整洁的库:
https://github.com/serkanyersen/ifvisible.js
示例:
// If page is visible right now
if( ifvisible.now() ){
// Display pop-up
openPopUp();
}
我已经在我拥有的所有浏览器上测试了版本1.0.1,并且可以确认它适用于:
......可能还有所有新版本。
不能完全使用:
.now()
始终为我返回true
)答案 4 :(得分:13)
我为我的应用创建了一个Comet Chat,当我收到来自其他用户的消息时,我使用:
if(new_message){
if(!document.hasFocus()){
audio.play();
document.title="Have new messages";
}
else{
audio.stop();
document.title="Application Name";
}
}
答案 5 :(得分:13)
document.addEventListener( 'visibilitychange' , function() {
if (document.hidden) {
console.log('bye');
} else {
console.log('well back');
}
}, false );
答案 6 :(得分:10)
我开始使用社区维基回答,但意识到它没有在Chrome中检测alt-tab事件。这是因为它使用了第一个可用的事件源,在这种情况下,它是页面可见性API,在Chrome中似乎无法跟踪alt-tabbing。
我决定稍微修改脚本以跟踪所有可能的事件以进行页面焦点更改。这是一个你可以参与的功能:
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) {
document.addEventListener('visibilitychange',
function() {(document.hidden ? unfocused : focused)()});
} else if ('mozHidden' in document) {
document.addEventListener('mozvisibilitychange',
function() {(document.mozHidden ? unfocused : focused)()});
} else if ('webkitHidden' in document) {
document.addEventListener('webkitvisibilitychange',
function() {(document.webkitHidden ? unfocused : focused)()});
} else if ('msHidden' in document) {
document.addEventListener('msvisibilitychange',
function() {(document.msHidden ? unfocused : focused)()});
} else if ('onfocusin' in document) {
// IE 9 and lower:
document.onfocusin = focused;
document.onfocusout = unfocused;
} else {
// All others:
window.onpageshow = window.onfocus = focused;
window.onpagehide = window.onblur = unfocused;
}
};
像这样使用:
onVisibilityChange(function(visible) {
console.log('the page is now', visible ? 'focused' : 'unfocused');
});
答案 7 :(得分:7)
这真的很棘手。鉴于以下要求,似乎没有解决方案。
这是因为:
鉴于这些限制,可以实现结合的解决方案 - 可见性API页面 - 窗口模糊/焦点 - document.activeElement
能够:
当iframe具有焦点时,您的模糊/焦点事件根本不会被调用,并且页面Visibility API不会在alt + tab上触发。
我在@ AndyE的解决方案的基础上构建并实现了这个(几乎不错的)解决方案: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (对不起,我在JSFiddle上遇到了一些麻烦)。
这也适用于Github:https://github.com/qmagico/estante-components
这适用于铬/铬。 它类似于firefox,除了它不加载iframe内容(任何想法为什么?)
无论如何,要解决最后一个问题(4),你唯一能做的就是在iframe上监听模糊/焦点事件。 如果您对iframe有一定的控制权,则可以使用postMessage API来执行此操作。
https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html
我还没有用足够的浏览器测试过这个。 如果您能找到更多关于这不起作用的信息,请在下面的评论中告诉我。
答案 8 :(得分:4)
var visibilityChange = (function (window) {
var inView = false;
return function (fn) {
window.onfocus = window.onblur = window.onpageshow = window.onpagehide = function (e) {
if ({focus:1, pageshow:1}[e.type]) {
if (inView) return;
fn("visible");
inView = true;
} else if (inView) {
fn("hidden");
inView = false;
}
};
};
}(this));
visibilityChange(function (state) {
console.log(state);
});
答案 9 :(得分:3)
在HTML 5中,您还可以使用:
onpageshow
:窗口可见时要运行的脚本onpagehide
:隐藏窗口时要运行的脚本请参阅:
答案 10 :(得分:3)
这适用于Chrome 67,Firefox 67,
if(!document.hasFocus()) {
// do stuff
}
答案 11 :(得分:3)
(function () {
var requiredResolution = 10; // ms
var checkInterval = 1000; // ms
var tolerance = 20; // percent
var counter = 0;
var expected = checkInterval / requiredResolution;
//console.log('expected:', expected);
window.setInterval(function () {
counter++;
}, requiredResolution);
window.setInterval(function () {
var deviation = 100 * Math.abs(1 - counter / expected);
// console.log('is:', counter, '(off by', deviation , '%)');
if (deviation > tolerance) {
console.warn('Timer resolution not sufficient!');
}
counter = 0;
}, checkInterval);
})();
答案 12 :(得分:2)
稍微复杂一点的方法是使用setInterval()
检查鼠标位置并与上次检查进行比较。如果鼠标未在设定的时间内移动,则用户可能处于空闲状态。
这样做的另一个好处就是告诉用户是否空闲,而不是只是检查窗口是否处于活动状态。
正如许多人所指出的,这并不总是检查用户或浏览器窗口是否空闲的好方法,因为用户可能甚至没有使用鼠标或正在观看视频或类似内容。我只是建议一种可能的方法来检查闲置。
答案 13 :(得分:2)
这是对Andy E答案的改编。
这将执行一项任务,例如每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);
}
答案 14 :(得分:1)
对于angular.js,这是一个指令(基于接受的答案),它将允许您的控制器对可见性的变化作出反应:
myApp.directive('reactOnWindowFocus', function($parse) {
return {
restrict: "A",
link: function(scope, element, attrs) {
var hidden = "hidden";
var currentlyVisible = true;
var functionOrExpression = $parse(attrs.reactOnWindowFocus);
// 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);
else if ("onfocusin" in document) {
// IE 9 and lower:
document.onfocusin = onshow;
document.onfocusout = onhide;
} else {
// All others:
window.onpageshow = window.onfocus = onshow;
window.onpagehide = window.onblur = onhide;
}
function onchange (evt) {
//occurs both on leaving and on returning
currentlyVisible = !currentlyVisible;
doSomethingIfAppropriate();
}
function onshow(evt) {
//for older browsers
currentlyVisible = true;
doSomethingIfAppropriate();
}
function onhide(evt) {
//for older browsers
currentlyVisible = false;
doSomethingIfAppropriate();
}
function doSomethingIfAppropriate() {
if (currentlyVisible) {
//trigger angular digest cycle in this scope
scope.$apply(function() {
functionOrExpression(scope);
});
}
}
}
};
});
您可以像以下示例一样使用它:<div react-on-window-focus="refresh()">
,其中refresh()
是范围内的函数,范围内的控制器范围。
答案 15 :(得分:1)
对于没有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&lt; 7)也可用
答案 16 :(得分:1)
这对我有用
document.addEventListener("visibilitychange", function() {
document.title = document.hidden ? "I'm away" : "I'm here";
});
答案 17 :(得分:0)
如果您想 整个浏览器模糊: 正如我评论的那样,如果浏览器松散焦点,则没有任何建议事件触发。我的想法是在循环中计数并在事件触发时重置计数器。如果计数器达到限制,我会将location.href用于其他页面。如果您使用开发工具,这也会激发。
var iput=document.getElementById("hiddenInput");
,count=1
;
function check(){
count++;
if(count%2===0){
iput.focus();
}
else{
iput.blur();
}
iput.value=count;
if(count>3){
location.href="http://Nirwana.com";
}
setTimeout(function(){check()},1000);
}
iput.onblur=function(){count=1}
iput.onfocus=function(){count=1}
check();
这是在FF上成功测试的草案。
答案 18 :(得分:0)
只想添加: 问题不清楚。 “当用户没有看到该网站时(即窗口或标签没有焦点)......”
我可以在没有焦点的情况下查看网站。大多数桌面系统都能够并行显示窗口:)
这就是为什么页面可见性API可能是正确答案的原因,因为它阻止在“用户无法看到更新”时更新网站,这与“标签没有焦点”非常不同。
答案 19 :(得分:0)
这是一个可靠的现代解决方案。 (甜蜜的Sh短)
document.addEventListener("visibilitychange", () => {
console.log( document.hasFocus() )
})
这将设置一个侦听器,以在激发可见性事件(可能是焦点或模糊)时触发。
答案 20 :(得分:0)
Chromium 团队目前正在开发 Idle Detection API。它可以作为 origin trial since Chrome 88 使用,这已经是此功能的第二个原始试用版。较早的原始试用版是从 Chrome 84 到 Chrome 86。
也可以通过标志启用:
<块引用>在本地试验 空闲检测 API,无需
原始试用令牌,启用
#enable-experimental-web-platform-features
标志
chrome://flags。
可以在此处找到演示:
<块引用>https://idle-detection.glitch.me/
必须注意的是,该 API 是基于权限的(理应如此,否则可能会被滥用来监控用户的行为!)。