22

我正在 Chrome 版本 42.0.2311.152m 下进行测试,我想实现在通知点击上打开一个窗口,如下例所示:(来源:https ://developer.mozilla.org/en-US/docs/Web/API /窗口客户端

self.addEventListener('notificationclick', function(event) {
  console.log('On notification click: ', event.notification.tag);
  event.notification.close();

  // This looks to see if the current is already open and
  // focuses if it is
  event.waitUntil(clients.matchAll({
    type: "window"
  }).then(function(clientList) {
    for (var i = 0; i < clientList.length; i++) {
      var client = clientList[i];
      if (client.url == '/' && 'focus' in client)
        return client.focus();
    }
    if (clients.openWindow)
      return clients.openWindow('/');
  }));
});

我的文件结构是这样的:
https://myurl.no-ip.org/app/index.html
https://myurl.no-ip.org/app/manifest.json
https://myurl.no-ip.org /app/service-worker.js

我有一个问题,我总是得到一个

无效访问错误

在 service-worker.js 中调用 clients.openWindow('/') 或 clients.openWindow(' https://myurl.no-ip.org/app/index.html ') 时,我收到错误消息:

{code: 15,
message: "Not allowed to open a window.",
name: "InvalidAccessError"}

永远不会到达“return client.focus()”行,因为 client.url 永远不会只是'/'。看着

clients.matchAll({type: "window"})
.then(function (clientList) {
console.log(clientList[0])});

我看到了我当前的 WindowClient:

{focused: false,
frameType: "top-level",
url: "https://myurl.no-ip.org/app/index.html",
visibilityState: "hidden" }

'focused' 和 'visibilityState' 属性是正确的并且可以正确更改。
通过进行手动焦点调用

clients.matchAll({type: "window"})
    .then(function (clientList) {
    clientList[0].focus()});

我收到错误:

{code: 15,
message: "Not allowed to focus a window.",
name: "InvalidAccessError"}

我认为问题在于 url 不仅仅是'/'。你对此有什么想法吗?

非常感谢!
最好的问候
安迪

4

1 回答 1

51

您的代码对我来说很好,所以我将解释使用openWindow/的要求focus,以及如何避免“不允许 [open|focus] 窗口”错误消息。

clients.openWindow()并且windowClient.focus()仅在单击通知后才被允许(至少在 Chrome 47 中),并且在单击处理程序的持续时间内最多可以调用其中一种方法。此行为在https://github.com/slightlyoff/ServiceWorker/issues/602中指定。

如果您的openWindow/focus呼叫被拒绝并显示错误消息

“不许开窗。” 对于openWindow
“不允许聚焦窗口”。为了focus

那么你没有满足openWindow/的要求focus。例如(所有点也适用于focus,而不仅仅是openWindow)。

  • openWindow在未点击通知时调用。
  • openWindownotificationclick处理程序返回后调用,并且您没有event.waitUntil使用承诺调用。
  • openWindowevent.waitUntil在传递给的承诺被解决后被调用。
  • 承诺没有解决,但它花了“太长时间”(在 Chrome 中为 10 秒),所以临时调用权限openWindow已过期。

确实有必要最多调用一次 / 并且在openWindow处理程序完成之前调用。focusnotificationclick

正如我之前所说,问题中的代码有效,所以我将展示另一个带注释的示例。

// serviceworker.js
self.addEventListener('notificationclick', function(event) {
    // Close notification.
    event.notification.close();

    // Example: Open window after 3 seconds.
    // (doing so is a terrible user experience by the way, because
    //  the user is left wondering what happens for 3 seconds.)
    var promise = new Promise(function(resolve) {
        setTimeout(resolve, 3000);
    }).then(function() {
        // return the promise returned by openWindow, just in case.
        // Opening any origin only works in Chrome 43+.
        return clients.openWindow('https://example.com');
    });

    // Now wait for the promise to keep the permission alive.
    event.waitUntil(promise);
});

index.html

<button id="show-notification-btn">Show notification</button>
<script>
navigator.serviceWorker.register('serviceworker.js');
document.getElementById('show-notification-btn').onclick = function() {
    Notification.requestPermission(function(result) {
        // result = 'allowed' / 'denied' / 'default'
        if (result !== 'denied') {
            navigator.serviceWorker.ready.then(function(registration) {
                // Show notification. If the user clicks on this
                // notification, then "notificationclick" is fired.
                registration.showNotification('Test');
            });
        }
    });
}
</script>

PS。Service Worker 仍在开发中,所以值得一提的是,我已经验证上述备注在 Chrome 49 中是正确的,并且该示例在 Chrome 43+ 中有效(并且在 Chrome 42/中也可以打开而不是打开https://example.com)。

于 2015-12-13T10:47:22.827 回答