使用通知 API

安全上下文:此功能仅在安全上下文(HTTPS)中可用,在一些或所有支持的浏览器中。

注意:此功能在Web Workers中可用。

通知 API 允许网页或应用程序发送在系统级页面外部显示的通知;这使 Web 应用程序能够向用户发送信息,即使应用程序处于空闲状态或后台状态。本文介绍了如何在您自己的应用程序中使用此 API 的基础知识。

通常,系统通知是指操作系统的标准通知机制:例如,典型的桌面系统或移动设备如何广播通知。

Desktop notification: To do list via mdn.github.io HEY! Your task "Go shopping" is now overdue

系统通知系统当然会因平台和浏览器而异,但这没关系,通知 API 被编写得足够通用,以与大多数系统通知系统兼容。

示例

Web 通知最明显的用例之一是基于 Web 的邮件或 IRC 应用程序,该应用程序需要在收到新消息时通知用户,即使用户正在使用其他应用程序进行其他操作。现在,这类应用程序有很多例子,例如 Slack

我们编写了一个现实世界中的例子——一个待办事项列表应用程序——以更多地说明如何使用 Web 通知。它使用 IndexedDB 在本地存储数据,并在任务到期时使用系统通知通知用户。 下载待办事项列表代码,或 查看正在运行的应用程序

请求权限

在应用程序可以发送通知之前,用户必须授予应用程序执行此操作的权利。当 API 尝试与网页外部的东西进行交互时,这是一个常见的要求——至少要有一次,用户需要明确地授予该应用程序显示通知的权限,从而让用户控制哪些应用程序/网站可以显示通知。

由于过去对推送通知的滥用,Web 浏览器和开发人员开始实施策略来帮助缓解这个问题。您只应在响应用户操作(例如,单击按钮)时请求显示通知的同意。这不仅是最佳实践——您不应该向用户发送他们不同意的通知——而且今后浏览器将明确禁止未响应用户操作触发的通知权限请求。例如,Firefox 从 72 版本开始就已经这样做,Safari 也已经这样做了一段时间了。

此外,在 Chrome 和 Firefox 中,除非网站是安全上下文(即 HTTPS),否则您根本无法请求通知,并且您不再允许从跨域<iframe>请求通知权限。

检查当前权限状态

您可以通过检查Notification.permission只读属性的值来查看您是否已经拥有权限。它可以具有以下三个可能的值之一

默认

用户尚未被要求提供权限,因此不会显示通知。

已授予

用户在之前被要求后已授予显示通知的权限。

拒绝

用户已明确拒绝显示通知的权限。

获取权限

如果尚未授予显示通知的权限,则应用程序需要使用 Notification.requestPermission() 方法向用户请求此权限。在最简单的情况下,我们只需包含以下内容

js
Notification.requestPermission().then((result) => {
  console.log(result);
});

这使用了该方法的基于 Promise 的版本。如果您想支持旧版本,您可能必须使用旧的回调版本,它看起来像这样

js
Notification.requestPermission((result) => {
  console.log(result);
});

回调版本可以选择接受一个回调函数,该函数在用户响应显示权限请求后调用。

注意:无法可靠地进行功能测试以确定 Notification.requestPermission 是否支持基于 Promise 的版本。如果您需要支持旧浏览器,只需使用基于回调的版本——尽管已弃用,但它在新的浏览器中仍然有效。查看浏览器兼容性表以了解更多信息。

示例

在我们的待办事项列表演示中,我们包含了一个“启用通知”按钮,当按下该按钮时,会请求应用程序的通知权限。

html
<button id="enable">Enable notifications</button>

单击此按钮将调用 askNotificationPermission() 函数

js
function askNotificationPermission() {
  // Check if the browser supports notifications
  if (!("Notification" in window)) {
    console.log("This browser does not support notifications.");
    return;
  }
  Notification.requestPermission().then((permission) => {
    // set the button to shown or hidden, depending on what the user answers
    notificationBtn.style.display = permission === "granted" ? "none" : "block";
  });
}

首先看一下第二个主要块,您会看到我们首先检查是否支持通知。如果支持,我们将运行 Notification.requestPermission() 的基于 Promise 的版本,如果不支持,我们将向控制台记录一条消息。

在传递给 then 的 Promise 解析处理程序内部,我们将根据用户在权限对话框中选择的内容显示或隐藏按钮。如果权限已授予,我们不想显示它,但如果用户选择拒绝权限,我们想让他们以后有机会改变主意。

创建通知

创建通知很容易;只需使用 Notification 构造函数即可。此构造函数期望在通知中显示一个标题和一些选项来增强通知,例如 icon 或文本 body

例如,在待办事项列表示例中,我们使用以下代码段在需要时创建通知(在 createNotification() 函数中找到)

js
const img = "/to-do-notifications/img/icon-128.png";
const text = `HEY! Your task "${title}" is now overdue.`;
const notification = new Notification("To do list", { body: text, icon: img });

关闭通知

使用 close() 删除不再与用户相关的通知(例如,用户已经在网页上阅读了通知,在消息应用程序的情况下,或者下一首歌曲已经在音乐应用程序中播放,以通知歌曲更改)。大多数现代浏览器在几秒钟(大约四秒钟)后会自动关闭通知,但这不是您通常应该关注的事情,因为这取决于用户和用户代理。关闭也可能发生在操作系统级别,用户应该对此保持控制。旧版本的 Chrome 不会自动删除通知,因此您可以在 setTimeout() 之后仅为这些旧版本执行此操作,以防止从其他浏览器的通知托盘中删除通知。

js
const n = new Notification("My Great Song");
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "visible") {
    // The tab has become visible so clear the now-stale Notification.
    n.close();
  }
});

注意:此 API 不应仅用于在固定延迟后(在现代浏览器中)从屏幕中删除通知,因为此方法还会从任何通知托盘中删除通知,从而阻止用户在最初显示后与之交互。

注意:当您收到“关闭”事件时,无法保证是用户关闭了通知。这与规范一致,规范指出:“当通知被关闭时,无论是底层通知平台还是用户关闭的,都必须运行它的关闭步骤。”

通知事件

有四个事件在 Notification 实例上触发

点击

当用户单击通知时触发。

关闭

通知关闭后触发。

错误

如果通知出现问题,则触发;这通常是因为通知由于某种原因无法显示。

显示

当通知显示给用户时触发。

可以使用 onclickoncloseonerroronshow 处理程序跟踪这些事件。因为 Notification 也是 EventTarget 的继承者,因此可以使用 addEventListener() 方法在其上。

替换现有通知

通常情况下,用户不希望在短时间内收到很多通知——例如,如果一个消息应用程序为每条传入的消息通知用户,而他们正在收到很多消息怎么办?为了避免向用户发送太多通知,可以修改待处理通知队列,用新的通知替换单个或多个待处理通知。

为此,可以为任何新的通知添加一个标签。如果通知已具有相同的标签,并且尚未显示,则新的通知将替换该之前的通知。如果具有相同标签的通知已经显示,则关闭之前的通知,并显示新的通知。

标签示例

假设以下基本的 HTML 代码

html
<button id="notify">Notify me!</button>
<section id="demo-logs"></section>

可以使用这种方式处理多个通知

js
const demoLogs = document.querySelector("#demo-logs");

window.addEventListener("load", () => {
  const button = document.querySelector("#notify");

  button.addEventListener("click", () => {
    if (Notification?.permission === "granted") {
      demoLogs.innerText += `The site has permission to show notifications. Showing notifications.\n`;
      // If the user agreed to get notified
      // Let's try to send ten notifications
      let i = 0;
      // Using an interval cause some browsers (including Firefox) are blocking notifications if there are too much in a certain time.
      const interval = setInterval(() => {
        // Thanks to the tag, we should only see the "Hi no 9 from MDN." notification
        const n = new Notification(`Hi no ${i} from MDN.`, {
          tag: "soManyNotification",
        });
        if (i === 9) {
          clearInterval(interval);
        }
        i++;
      }, 200);
    } else if (Notification && Notification.permission !== "denied") {
      demoLogs.innerText += "Requesting notification permission.\n";
      // If the user hasn't told if they want to be notified or not
      // Note: because of Chrome, we are not sure the permission property
      // is set, therefore it's unsafe to check for the "default" value.
      Notification.requestPermission().then((status) => {
        // If the user said okay
        if (status === "granted") {
          demoLogs.innerText +=
            "User granted the permission. Sending notifications.\n";
          let i = 0;
          // Using an interval cause some browsers (including Firefox) are blocking notifications if there are too much in a certain time.
          const interval = setInterval(() => {
            // Thanks to the tag, we should only see the "Hi! 9" notification
            const n = new Notification(`Message no ${i} from MDN.`, {
              tag: "soManyNotification",
            });
            if (i === 9) {
              clearInterval(interval);
            }
            i++;
          }, 200);
        } else {
          // Otherwise, we can fallback to a regular modal alert
          demoLogs.innerText += `User denied the permission request.\n`;
        }
      });
    } else {
      // If the user refuses to get notified, we can fallback to a regular modal alert
      demoLogs.innerText += `The site does not have permission to show notifications.\n`;
    }
  });
});

结果

要测试上面的示例,请更改发送通知设置 for the https://live.mdnplay.dev 网站。

另请参阅