用iframe识别窗口聚焦/模糊事件



我试图可靠地识别何时浏览器窗口/选项卡被激活和停用。正常情况下,windowfocusblur事件可以,但是文档包含几个iframe。
当iframe被聚焦时,主窗口不聚焦,反之亦然,所以我们有以下聚焦事件的可能性[(none)意味着窗口/选项卡被停用]:

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

注册所有这些事件没有问题,如下图所示。但是,每当我们从主窗口切换到iframe或反之亦然,或者在两个iframe之间切换时,各自的blurfocus事件都会触发;他们会稍微延迟一下就开火。

我担心这里的并发性,因为blur处理器可以去开始做事情,但它应该永远不会开始,因为用户实际上只是在帧之间的某个地方切换焦点。
示例:页面应该在当前不活动时周期性地执行一些AJAX请求。也就是说,它应该在用户停用选项卡时开始请求,并在再次激活选项卡时停止请求。因此,我们将一个函数绑定到发起请求的blur事件。如果用户只是单击另一个iframe blur,然后不久,就会触发focus。但是blur处理程序已经启动,在再次停止之前至少发出一个请求。

这就是我的问题:我如何才能可靠地检测用户何时实际(取消)激活包含iframes的浏览器窗口,而不会冒着由两个直接blurfocus事件引起的假警报的风险?

我写了一个不成熟的解决方案,在blur事件之后使用超时,以确定在它之后是否有立即的focus事件(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);
    });
});

但是我认为这可能会有很大的问题,特别是在较慢的机器上。增加超时时间对我来说也是不可接受的,50毫秒真的是我的疼痛阈值

有没有一种方法,不依赖于客户端是足够快?

您可以轮询document.hasFocus()值,如果iframe或主窗口被聚焦,该值应该为true

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') }

我试图在没有轮询的情况下做到这一点,但iframe不会触发onblur事件(如果浏览器窗口在iframe处于焦点时停用,我不会触发任何事件),所以我最终需要轮询一半,但也许有人可以用这段代码找出一些东西

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

最新更新