使用ALT + TAB切换程序/窗口或单击任务栏时,不会触发visibilitychange事件

时间:2015-03-11 17:17:06

标签: javascript html5 events javascript-events event-handling

基本上,问题在于事件的行为" visibilitychange"。

已触发:   - 当我切换到浏览器窗口内的其他选项卡时。

  • 当我点击浏览器窗口的最小化/恢复按钮时。

(这没关系)

未触发:   - 当我使用ALT + TAB切换到不同的窗口/程序时。

  • 当我切换到另一个窗口/程序时,单击任务栏。

(这应该触发,因为,就像最小化时一样,窗口的可见性可能会发生变化)


W3页面可见性API文档http://www.w3.org/TR/page-visibility/

没有定义"页面可见性"关于规格表中的ALT + TAB /程序切换。我猜测它在操作系统和浏览器之间有一些关系。


测试

  • 浏览器: Chrome 40.0.2214.115 m / Firefox 36.0.1 / Internet Explorer 11.0.9600.17107
  • 操作系统:Windows 8.1

是否有解决此问题的解决方法?实现相当简单,我听" visibilitychange"使用jQuery的事件,然后在其回调中,我检查" document.visibilityState"的值,但问题是事件在预期时没有触发。

$(document).on('visibilitychange', function() {

    if(document.visibilityState == 'hidden') {
        // page is hidden
    } else {
        // page is visible
    }
});

这可以在没有jQuery的情况下完成,但ALT + TAB和任务栏开关隐藏/显示预期的行为仍然缺失:

if(document.addEventListener){
    document.addEventListener("visibilitychange", function() {
        // check for page visibility
    });
}

我也尝试了ifvisible.js模块(https://github.com/serkanyersen/ifvisible.js),但行为是一样的。

ifvisible.on('blur', function() {
    // page is hidden
});

ifvisible.on('focus', function() {
    // page is visible
});

我还没有在其他浏览器中测试过,因为如果我无法在Windows上使用Chrome,我真的不在乎其他浏览器。

提前感谢任何帮助或建议。


更新

我尝试使用不同的供应商前缀作为事件名称(visibilitychange,webkitvisibilitychange,mozvisibilitychange,msvisibilitychange),但是当我切换到任务栏中的其他程序或ALT + TAB时,或者即使我打开时,仍未触发事件Windows窗口中的开始菜单用windows键覆盖整个屏幕。

我可以在Chrome,Firefox和Internet Explorer中重现完全相同的问题。

更新#2

Here's我为此问题撰写的综合帖子以及纯JavaScript中的解决方法,以解决遇到的问题。

更新#3

编辑包含源博客文章的副本。 (见接受的答案)

4 个答案:

答案 0 :(得分:10)

Here's我为此问题撰写的综合帖子以及纯JavaScript中的解决方法,以解决遇到的问题。

已修改为包含源文章的副本:


在我们开发的任何类型的javascript应用程序中,可能存在根据当前用户可见性状态作出反应的应用程序中的功能或任何更改,这可以是当用户ALT + TAB到不同窗口时暂停播放视频,跟踪有关用户如何与我们的应用程序交互的统计信息,他切换到不同选项卡的频率,他返回需要多长时间以及可以从这种API中受益的大量性能改进。

Page Visibility API为我们提供了两个顶级属性:document.hidden(boolean)和document.visibilityState(可以是以下任何字符串:“hidden”,“visible”,“prerender”,“unloaded” )。如果没有我们可以听到的事件,这就不够好,这就是为什么API也提供了有用的visibilitychange事件。

所以,这是一个关于我们如何对可见性变化采取行动的基本示例:

function handleVisibilityChange() {
  if(document.hidden) {
    // the page is hidden
  } else {
    // the page is visible
  }
}

document.addEventListener("visibilitychange", handleVisibilityChange, false);

我们还可以检查document.visibilityState值。

处理供应商问题 John Smibert的George Berkeley

某些浏览器上的某些实现仍然需要属性甚至事件名称都是以供应商为前缀的,这意味着我们可能需要监听msvisibilitychange事件或检查document.webkitHidden或document.mozHidden属性。为了做到这一点,我们应该检查是否设置了任何vendor-prefixed属性,一旦我们知道哪一个是当前浏览器中使用的属性(只有在需要前缀的情况下),我们才能命名事件和属性正常。

以下是如何处理这些前缀的示例方法:

var browserPrefixes = ['moz', 'ms', 'o', 'webkit'];

// get the correct attribute name
function getHiddenPropertyName(prefix) {
  return (prefix ? prefix + 'Hidden' : 'hidden');
}

// get the correct event name
function getVisibilityEvent(prefix) {
  return (prefix ? prefix : '') + 'visibilitychange';
}

// get current browser vendor prefix
function getBrowserPrefix() {
  for (var i = 0; i < browserPrefixes.length; i++) {
    if(getHiddenPropertyName(browserPrefixes[i]) in document) {
      // return vendor prefix
      return browserPrefixes[i];
    }
  }

  // no vendor prefix needed
  return null;
}

// bind and handle events
var browserPrefix = getBrowserPrefix();

function handleVisibilityChange() {
  if(document[getHiddenPropertyName(browserPrefix )]) {
    // the page is hidden
    console.log('hidden  } else {
    // the page is visible
    console.log('hidden  }
}

document.addEventListener(getVisibilityEvent(browserPrefix), handleVisibilityChange, false);

其他问题 “页面可见性”定义存在一个具有挑战性的问题:如果另一个窗口的窗口焦点丢失,如何确定应用程序是否可见,而不是屏幕上的实际可见性?如何丢失不同类型的可见性,如ALT + TAB,WIN / MAC键(开始菜单/短划线),任务栏/停靠点操作,WIN + L(锁定屏幕),窗口最小化,窗口关闭,标签切换。移动设备上的行为怎么样?

我们可能有很多方法会丢失或获得浏览器与操作系统之间的可见性和可能的​​交互,因此我认为在W3C规范中没有一个正确且完整的“可见页面”定义。这是我们为document.hidden属性获得的定义:

HIDDEN ATTRIBUTE 获取时,如果顶级浏览上下文(浏览器视口中的根窗口)[HTML5]包含的文档根本不可见,则隐藏属性必须返回true。如果顶级浏览上下文包含的文档在至少一个屏幕上至少部分可见,则该属性必须返回false。

如果Document的defaultView为null,则在获取时,隐藏属性必须返回true。

为了容纳通常为全屏但仍显示页面视图的辅助功能工具,在适用时,当用户代理未最小化但被其他应用程序完全遮挡时,此属性可能会返回false。

我发现事件实际被触发时有几处不一致,例如(Chrome 41.0.2272.101 m,在Windows 8.1上)当我ALT + TAB到另一个窗口/程序时我也没有触发该事件+ TAB再次返回,但如果我按CTRL + TAB然后按CTRL + SHIFT + TAB在浏览器选项卡之间切换,则会被触发。当我点击最小化按钮时它也会被触发,但是如果窗口没有最大化则不会触发它,我点击了我的编辑器窗口,它正在浏览浏览器窗口。因此,这个API的行为及其不同的实现仍然模糊不清。

解决这个问题的方法是补偿利用更好的实现焦点和模糊事件,并使用内部标志自定义处理整个“页面可见性”问题以防止多次执行,这就是我所做的想出来:

var browserPrefixes = ['moz', 'ms', 'o', 'webkit'],
    isVisible = true; // internal flag, defaults to true

// get the correct attribute name
function getHiddenPropertyName(prefix) {
  return (prefix ? prefix + 'Hidden' : 'hidden');
}

// get the correct event name
function getVisibilityEvent(prefix) {
  return (prefix ? prefix : '') + 'visibilitychange';
}

// get current browser vendor prefix
function getBrowserPrefix() {
  for (var i = 0; i < browserPrefixes.length; i++) {
    if(getHiddenPropertyName(browserPrefixes[i]) in document) {
      // return vendor prefix
      return browserPrefixes[i];
    }
  }

  // no vendor prefix needed
  return null;
}

// bind and handle events
var browserPrefix = getBrowserPrefix(),
    hiddenPropertyName = getHiddenPropertyName(browserPrefix),
    visibilityEventName = getVisibilityEvent(browserPrefix);

function onVisible() {
  // prevent double execution
  if(isVisible) {
    return;
  }

  // change flag value
  isVisible = true;
  console.log('visible}

function onHidden() {
  // prevent double execution
  if(!isVisible) {
    return;
  }

  // change flag value
  isVisible = false;
  console.log('hidden}

function handleVisibilityChange(forcedFlag) {
  // forcedFlag is a boolean when this event handler is triggered by a
  // focus or blur eventotherwise it's an Event object
  if(typeof forcedFlag === "boolean") {
    if(forcedFlag) {
      return onVisible();
    }

    return onHidden();
  }

  if(document[hiddenPropertyName]) {
    return onHidden();
  }

  return onVisible();
}

document.addEventListener(visibilityEventName, handleVisibilityChange, false);

// extra event listeners for better behaviour
document.addEventListener('focus', function() {
  handleVisibilityChange(true);
}, false);

document.addEventListener('blur', function() {
  handleVisibilityChange(false);
}, false);

window.addEventListener('focus', function() {
    handleVisibilityChange(true);
}, false);

window.addEventListener('blur', function() {
  handleVisibilityChange(false);
}, false);

我欢迎任何有关此解决方法的反馈。关于这个主题的一些其他很好的想法来源:

使用Page Visibility API 在HTML5中更有效地使用PC硬件:新的Web性能API,第2部分 Page Visibility API简介 结论 网络的技术在不断发展,我们仍在从一个黑暗的过去中恢复,在这里,标记之王,语义不重要,而且它们没有关于浏览器如何呈现页面的任何标准。

我们推进这些新标准非常重要,但有时我们的开发要求仍然需要通过处理供应商前缀,在不同浏览器和不同操作系统中进行测试或依赖第三方工具来适应这些过渡找出这种差异。

我们只能希望未来的W3C规范经过严格修订,严格由浏览器开发团队实施,也许有一天我们将有一个共同的标准供我们所有人使用。

至于Page Visibility API,让我们引用George Berkeley并说:

人们正在感受到“被人看见”。

答案 1 :(得分:2)

这里描述了一个工作解决方案:https://stackoverflow.com/a/9502074/698168。它结合使用W3C页面可见性API,模糊/聚焦和鼠标移动。与Alt + Tab相关的隐藏HTML页面以概率方式识别(即,您无法确定您的页面是否以100%的准确度隐藏)。

答案 2 :(得分:1)

在选项卡之间切换和应用程序之间切换时,我们可以像下面这样

 var pageVisible = true;  
 function handleVisibilityChange() {
      if (document.hidden) {
        pageVisible = false;
      } else  {
        pageVisible = true;
      }
      console.log("handleVisibilityChange")
      console.log("pageVisible", pageVisible)
      // some function call
    }
    document.addEventListener("visibilitychange", handleVisibilityChange, false);
    window.addEventListener('focus', function() {
        pageVisible = true;
        // some function call 
    }, false);
    window.addEventListener('blur', function() {
      pageVisible = false;
      // some function call  
    }, false);

答案 3 :(得分:0)

我遇到过一个非常简单的解决方案。

您需要在将事件侦听器附加到文档时将false传递给useCapture。像魅力一样工作!

chrome.tabs.executeScript(tabs[0].id, {file: "xyz.js"});