【问题标题】:visibilitychange event is not triggered when switching program/window with ALT+TAB or clicking in taskbar使用 ALT+TAB 切换程序/窗口或单击任务栏时不会触发 visibilitychange 事件
【发布时间】:2015-05-13 15:05:09
【问题描述】:

问题在于事件“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
    }
});

这也可以在没有 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

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

【问题讨论】:

标签: javascript html events event-handling dom-events


【解决方案1】:

Here's 我为这个问题写的一篇综述文章,以及一个用纯 JavaScript 解决遇到的问题的变通方法。

已编辑以包含来源博客文章的副本:


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

Page Visibility API 为我们提供了两个顶级属性: document.hidden (boolean) 和 document.visibilityState (可能是 这些字符串中的任何一个:“hidden”、“visible”、“prerender”、“unloaded”)。 如果没有我们可以收听的事件,这还不够好 不过,这就是 API 还提供有用的可见性更改的原因 事件。

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

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

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

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

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

某些浏览器上的某些实现仍然需要 属性甚至事件名称都是以供应商为前缀的,这意味着我们 可能需要监听 msvisibilitychange 事件或检查 document.webkitHidden 或 document.mozHidden 属性。为了 为此,我们应该检查是否设置了任何以供应商为前缀的属性,并且 一旦我们知道哪个是当前浏览器中使用的(仅当 需要前缀),我们可以命名事件和属性 正确。

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

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

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

其他问题围绕“页面可见性”存在一个具有挑战性的问题 定义:如何判断应用是否可见 另一个窗口的窗口焦点丢失了,但不是实际的 屏幕上的可见性?不同种类的能见度怎么样 丢失,如 ALT+TAB、WIN/MAC 键(开始菜单/破折号)、任务栏/停靠 操作、WIN+L(锁定屏幕)、窗口最小化、窗口关闭、选项卡 交换。移动设备上的行为呢?

我们可能会通过多种方式失去或获得知名度,还有很多 浏览器和操作系统之间可能的交互,因此我 不要认为在 W3C 规范。这是我们为 document.hidden 得到的定义 属性:

隐藏属性 在获取时,隐藏属性必须返回 true,如果 顶级浏览上下文(根窗口)包含的文档 在浏览器的视口中)[HTML5] 根本不可见。这 如果顶层包含的 Document 属性必须返回 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);

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

使用页面可见性 API 更有效地使用 PC 硬件 HTML5:新的 Web 性能 API,第 2 部分页面介绍 可见性 API 结论 不断发展,我们仍在从黑暗的过去中恢复过来 标记之王,语义无关紧要,它们也不重要 浏览器应如何呈现页面的标准。

我们推动这些新标准向前发展很重要,但有时我们的 发展需求让我们还是需要适应这些 转换,通过处理供应商前缀,在不同的测试 浏览器和不同的操作系统或依赖第三方工具正确 找出这些差异。

我们只能寄希望于 W3C 规范的未来 严格修改,由浏览器开发团队严格执行, 也许有一天我们会有一个共同的标准让我们所有人工作 与。

至于页面可见性 API,让我们稍微引用一​​下 George Berkeley 并说:

“可见”正在被感知。

【讨论】:

  • 这是在答案中实际给出答案有益的情况之一(链接的网站已死)。
  • 对不起,我修复了链接,帖子是stereologics.wordpress.com/2015/04/02/…
  • @agbb 正如他所说,您需要给出实际答案而不是链接。链接消失(曾经发生过,并且会再次发生)
  • blur 事件也会在 alert()、prompt() 和 confirm() 上触发.. 有什么方法可以限制吗?
【解决方案2】:

这里提出了一个可行的解决方案:https://stackoverflow.com/a/9502074/698168。它结合了 W3C Page Visibility API、模糊/焦点和鼠标移动。与 Alt+Tab 相关的隐藏 HTML 页面以概率方式识别(即您无法确定您的页面是否以 100% 的准确度隐藏)。

【讨论】:

  • 赞成,因为您链接到我之前在做相关工作时赞成的答案。那是因为这里选择的答案只有 7 票,而且看起来肯定像一个兔子洞。
【解决方案3】:

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

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

 document.addEventListener('visibilitychange', function () {
  // code goes here
}, false)

【讨论】:

  • 我认为这可能与页面可见性规范 (w3.org/TR/page-visibility) 的最新版本有关,在相对较新的浏览器上,这可能比原始问题中描述的效果更好。浏览器兼容性图表见developer.mozilla.org/en-US/docs/Web/Events/visibilitychange
  • 我把它设置为 false,但它仍然无法在最新的 Chrome (56.0) 或最新的 Firefox (52.0) 中运行
  • False 是默认值,所以这不会改变任何东西。
  • 在 Vivaldi 2.3.1440.60 中测试没有成功。
  • 不足之处在上面接受的解决方案中进行了讨论,简而言之,在您切换程序/应用程序时不考虑这一点,仅在选项卡之间切换。
【解决方案4】:

在标签页之间切换和应用程序之间切换时,我们可以像下面这样操作

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多