有没有办法检测浏览器窗口当前是否处于活动状态?

我有定期执行活动的JavaScript。当用户不查看网站时(即窗口或选项卡没有焦点),不运行会很好。

有没有办法使用JavaScript来做到这一点?

我的参考点:如果您使用的窗口未激活,Gmail Chat会播放声音。

381832 次浏览

自从最初编写这个答案以来,由于W3C,一个新规范已经达到了建议状态。页面可见性API(在MDN上)现在允许我们更准确地检测页面何时对用户隐藏。

document.addEventListener("visibilitychange", onchange);

当前浏览器支持:

  • Chrome13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+[阅读注释]

以下代码回退到不兼容浏览器中不太可靠的blur/Focus方法:

(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"});
})();

onfocusinonfocusout适用于IE 9及更低版本,而其他所有使用onfocusonblur,除了使用onpageshowonpagehide的iOS。

一种稍微复杂一点的方法是使用setInterval()检查鼠标位置并与上次检查进行比较。如果鼠标在设定的时间内没有移动,则用户可能处于空闲状态。

这有一个额外的好处,那就是告诉用户是否空闲,而不是只是检查窗口是否不活动。

正如许多人指出的那样,这并不总是检查用户或浏览器窗口是否空闲的好方法,因为用户甚至可能没有使用鼠标或正在观看视频,或者类似的。我只是建议一种检查空闲的可能方法。

在超文本标记语言5中,您还可以使用:

  • onpageshow:当窗口变得可见时运行的脚本
  • onpagehide:隐藏窗口时运行的脚本

见:

我会使用jQuery,因为你所要做的就是:

$(window).blur(function(){
//your code here
});
$(window).focus(function(){
//your code
});

或者至少它对我有用。

有三种典型的方法用于确定用户是否可以看到超文本标记语言页面,但是没有一种方法可以完美地工作:

  • W3C页面可见性API应该这样做(支持:Firefox 10, MSIE 10,Chrome13)。但是,此API仅在浏览器选项卡完全覆盖时(例如当用户从一个选项卡更改为另一个选项卡时)引发事件。当可见性无法100%准确确定时(例如Alt+Tab切换到另一个应用程序),API不会引发事件。

  • 使用基于焦点/模糊的方法会给你很多误报。例如,如果用户在浏览器窗口顶部显示一个较小的窗口,浏览器窗口将失去焦点(onblur提出),但用户仍然能够看到它(所以它仍然需要刷新)。另见http://javascript.info/tutorial/focus

  • 依赖用户活跃度(鼠标移动、点击、键入键)也会给你很多误报。想想上面相同的情况,或者用户观看视频。

为了改善上述不完美的行为,我使用了3种方法的组合:W3C可见性API,然后聚焦/模糊和用户活动方法,以降低误判率。这允许管理以下事件:

  • 将浏览器选项卡更改为另一个选项卡(100%准确性,感谢W3C页面可见性API)
  • 页面可能被另一个窗口隐藏,例如由于Alt+Tab(概率=不是100%准确)
  • 用户的注意力可能没有集中在超文本标记语言页面上(概率=不是100%准确)

它是这样工作的:当文档失去焦点时,监控用户在文档上的活动(例如鼠标移动),以确定窗口是否可见。页面可见概率与页面上最后一次用户活动的时间成反比:如果用户在文档上长时间没有活动,则页面很可能不可见。下面的代码模仿W3C页面可见性API:它的行为方式相同,但误判率很小。它具有多浏览器的优势(在Firefox 5、Firefox 10、MSIE 9、MSIE 7、Safari5、Chrome9上测试)。


<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>


由于目前没有有效的跨浏览器解决方案而没有误报,因此您最好三思而后行禁用网站上的定期活动。

这是对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);
}

u可以使用:

(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);


})();
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);
});

http://jsfiddle.net/ARTsinn/JTxQY/

我为我的应用程序创建了Comet Chat,当我收到来自另一个用户的消息时,我使用:

if(new_message){
if(!document.hasFocus()){
audio.play();
document.title="Have new messages";
}
else{
audio.stop();
document.title="Application Name";
}
}

这真的很棘手。鉴于以下要求,似乎没有解决方案。

  • 该页面包含您无法控制的iFrame
  • 您希望跟踪可见性状态更改,而不管更改是由选项卡更改(ctrl+tab)还是窗口更改(alt+tab)触发的

这是因为:

  • 页面可见性API可以可靠地告诉您选项卡更改(即使使用iFrame),但它不能告诉您用户何时更改窗口。
  • 监听窗口模糊/焦点事件可以检测alt+tab和ctrl+tab,只要ifram没有焦点。

鉴于这些限制,可以实现一个结合 -页面可见性API -窗口模糊/焦点 -document.active元素

这能够:

  • 1)父页面有焦点时ctrl+tab:是
  • 2)ctrl+tab当ifram有焦点:是
  • 3)alt+tab当父页面有焦点时:是
  • 4)alt+tab当ifram有焦点时:<--无赖

当ifram有焦点时,你的blur/焦点事件根本不会被调用,页面可见性API也不会在alt+tab上触发。

我构建了@AndyE的解决方案并在这里实现了这个(几乎很好)的解决方案: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (对不起,我有一些JSFiddle的麻烦)。

这也可以在Github上找到:https://github.com/qmagico/estante-components

这适用于chrome/chromium。 它可以在Firefox上运行,除了它不加载ifram内容(知道为什么吗?)

无论如何,要解决最后一个问题(4),您可以做到的唯一方法是侦听ifram上的blur/Focus事件。 如果您可以控制iframe,则可以使用postMessage API来执行此操作。

https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html

我还没有用足够的浏览器测试这个。 如果你能找到更多关于这不起作用的信息,请在下面的评论中告诉我。

在GitHub上有一个简洁的库:

https://github.com/serkanyersen/ifvisible.js

示例:

// If page is visible right now
if( ifvisible.now() ){
// Display pop-up
openPopUp();
}

我已经在我拥有的所有浏览器上测试了1.0.1版,并且可以确认它适用于:

  • IE9、IE10
  • FF 26.0
  • Chrome34.0

…可能是所有更新的版本。

不完全适用于:

  • IE8-始终指示选项卡/窗口当前处于活动状态(.now()始终为我返回true

对于没有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)的回退也可用

如果你想表演整个浏览器模糊: 正如我所评论的,如果浏览器失去焦点,建议的事件都不会着火。我的想法是在循环中计数,如果事件着火,重置计数器。如果计数器达到极限,我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上测试成功的草稿。

对于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()是任何Controller范围内的范围函数。

我开始使用社区wiki答案,但意识到它没有检测到Chrome中的alt-tab事件。这是因为它使用了第一个可用的事件源,在这种情况下是页面可见性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');
});

此版本侦听所有不同的可见性事件,并在其中任何事件导致更改时触发回调。focusedunfocused处理程序确保在多个API捕获相同的可见性更改时不会多次调用回调。

使用:页面可见性API

document.addEventListener( 'visibilitychange' , function() {
if (document.hidden) {
console.log('bye');
} else {
console.log('well back');
}
}, false );

我可以用吗?http://caniuse.com/#feat=pagevisibility

这适用于我的chrome 67,Firefox 67,

if(!document.hasFocus()) {
// do stuff
}

这是一个可靠的现代解决方案。(简短的👌🏽)

document.addEventListener("visibilitychange", () => {
console.log( document.hasFocus() )
})

这将设置一个侦听器,以便在触发任何可见性事件(可能是焦点或模糊)时触发。

这对我有用

document.addEventListener("visibilitychange", function() {
document.title = document.hidden ? "I'm away" : "I'm here";
});

演示:https://iamsahilralkar.github.io/document-hidden-demo/

Chromium团队目前正在开发空闲检测API。它可以作为88年Chrome以来的原产地审判使用,这已经是该功能的第二次原始试验。较早的原始试验从Chrome84到Chrome86。

它也可以通过标志启用:

通过chrome://标志启用

要在本地尝试空闲检测接口,无需 来源试用令牌,启用 #enable-experimental-web-platform-features标志在 chrome://标识.

演示可以在这里找到:

https://idle-detection.glitch.me/

但必须注意的是,这个API是基于权限(应该是这样,否则这可能会被滥用来监控用户的行为!)。

我重读了@丹尼尔-巴克马斯特版本 我没有进行多次尝试,但是,代码对我来说似乎更优雅……

// 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);
};
};
};

这适用于所有现代浏览器:

  • 更改标签时
  • 更改窗口时(Alt+Tab)
  • 从任务栏最大化另一个程序时
var eventName;
var visible = true;
var propName = "hidden";
if (propName in document) eventName = "visibilitychange";
else if ((propName = "msHidden") in document) eventName = "msvisibilitychange";
else if ((propName = "mozHidden") in document) eventName = "mozvisibilitychange";
else if ((propName = "webkitHidden") in document) eventName = "webkitvisibilitychange";
if (eventName) document.addEventListener(eventName, handleChange);


if ("onfocusin" in document) document.onfocusin = document.onfocusout = handleChange; //IE 9
window.onpageshow = window.onpagehide = window.onfocus = window.onblur = handleChange;// Changing tab with alt+tab


// Initialize state if Page Visibility API is supported
if (document[propName] !== undefined) handleChange({ type: document[propName] ? "blur" : "focus" });


function handleChange(evt) {
evt = evt || window.event;
if (visible && (["blur", "focusout", "pagehide"].includes(evt.type) || (this && this[propName]))){
visible = false;
console.log("Out...")
}
else if (!visible && (["focus", "focusin", "pageshow"].includes(evt.type) || (this && !this[propName]))){
visible = true;
console.log("In...")
}
}

我的代码

let browser_active = ((typeof document.hasFocus != 'undefined' ? document.hasFocus() : 1) ? 1 : 0);
if (!browser_active) {
// active
}

简单/立即检查:

if(document.hidden) {
// do something
}

可见性变更事件:

document.addEventListener("visibilitychange", function() {
console.log(document.visibilityState); // "hidden" or "visible"
}, false);

基于Promise的事件:

// 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");

(注意:我是将后两个解决方案添加到这个答案中的人,因为该问题现已关闭,我无法添加自己的答案。以防有人认为我从那篇文章中复制了它们而没有记入。)