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

agb*_*gbb 22 javascript events html5 event-handling javascript-events

基本上,问题在于事件"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

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

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

    if(document.visibilityState == 'hidden') {
        // page is hidden
    } else {
        // page is visible
    }
});
Run Code Online (Sandbox Code Playgroud)

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

if(document.addEventListener){
    document.addEventListener("visibilitychange", function() {
        // check for page visibility
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

ifvisible.on('focus', function() {
    // page is visible
});
Run Code Online (Sandbox Code Playgroud)

我没有在其他浏览器中测试过,因为如果我无法在Windows上的Chrome中运行,我真的不关心其他浏览器了.

任何帮助或建议都要提前感谢.


UPDATE

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

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

更新#2

这是我为这个问题写的综合帖子和纯javascript中的解决方法来解决遇到的问题.

更新#3

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

agb*_*gbb 13

这是我为这个问题写的综合帖子和纯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);
Run Code Online (Sandbox Code Playgroud)

我们还可以检查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);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

为了适应通常为全屏但仍显示页面视图的可访问性工具,在适用时,当用户代理未最小化但被其他应用程序完全遮盖时,此属性可能返回false.

我发现事件实际被触发时有几处不一致,例如(Chrome 41.0.2272.101 m,在Windows 8.1上)当我ALT + TAB到另一个窗口/程序时,当我再次ALT + 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);
Run Code Online (Sandbox Code Playgroud)

我欢迎任何关于此变通办法的反馈.关于这个主题的一些其他很好的想法来源:

使用页面可见性API在HTML5中更有效地使用PC硬件:新的Web性能API,第2部分页面可见性API简介结论Web的技术在不断发展,我们仍在从一个黑暗的过去中恢复,其中标记的表格王,语义没有关系,他们没有任何关于浏览器如何呈现页面的标准.

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

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

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

人们正在感受到"被人看见".

  • 这是在答案中实际给出答案是有益的(链接网站已经死亡)的情况之一. (3认同)
  • @agbb正如他所说,你需要给出实际答案而不是链接.链接死亡(发生一次,将再次发生) (2认同)
  • 如果链接确实失效:https://web.archive.org/web/20180912061844/https://stereologics.wordpress.com/2015/04/02/about-page-visibility-api-hidden-visibilitychange-visibilitystate / (2认同)

anj*_*505 5

在选项卡之间切换和应用程序之间切换时,我们可以执行如下操作

 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);
Run Code Online (Sandbox Code Playgroud)