使用通知 API
注意:此功能在Web Workers中可用。
通知 API 允许网页或应用程序发送在系统级页面外部显示的通知;这使 Web 应用程序能够向用户发送信息,即使应用程序处于空闲状态或后台状态。本文介绍了如何在您自己的应用程序中使用此 API 的基础知识。
通常,系统通知是指操作系统的标准通知机制:例如,典型的桌面系统或移动设备如何广播通知。
系统通知系统当然会因平台和浏览器而异,但这没关系,通知 API 被编写得足够通用,以与大多数系统通知系统兼容。
示例
Web 通知最明显的用例之一是基于 Web 的邮件或 IRC 应用程序,该应用程序需要在收到新消息时通知用户,即使用户正在使用其他应用程序进行其他操作。现在,这类应用程序有很多例子,例如 Slack。
我们编写了一个现实世界中的例子——一个待办事项列表应用程序——以更多地说明如何使用 Web 通知。它使用 IndexedDB 在本地存储数据,并在任务到期时使用系统通知通知用户。 下载待办事项列表代码,或 查看正在运行的应用程序。
请求权限
在应用程序可以发送通知之前,用户必须授予应用程序执行此操作的权利。当 API 尝试与网页外部的东西进行交互时,这是一个常见的要求——至少要有一次,用户需要明确地授予该应用程序显示通知的权限,从而让用户控制哪些应用程序/网站可以显示通知。
由于过去对推送通知的滥用,Web 浏览器和开发人员开始实施策略来帮助缓解这个问题。您只应在响应用户操作(例如,单击按钮)时请求显示通知的同意。这不仅是最佳实践——您不应该向用户发送他们不同意的通知——而且今后浏览器将明确禁止未响应用户操作触发的通知权限请求。例如,Firefox 从 72 版本开始就已经这样做,Safari 也已经这样做了一段时间了。
此外,在 Chrome 和 Firefox 中,除非网站是安全上下文(即 HTTPS),否则您根本无法请求通知,并且您不再允许从跨域<iframe>
请求通知权限。
检查当前权限状态
您可以通过检查Notification.permission
只读属性的值来查看您是否已经拥有权限。它可以具有以下三个可能的值之一
获取权限
如果尚未授予显示通知的权限,则应用程序需要使用 Notification.requestPermission()
方法向用户请求此权限。在最简单的情况下,我们只需包含以下内容
Notification.requestPermission().then((result) => {
console.log(result);
});
这使用了该方法的基于 Promise 的版本。如果您想支持旧版本,您可能必须使用旧的回调版本,它看起来像这样
Notification.requestPermission((result) => {
console.log(result);
});
回调版本可以选择接受一个回调函数,该函数在用户响应显示权限请求后调用。
注意:无法可靠地进行功能测试以确定 Notification.requestPermission
是否支持基于 Promise 的版本。如果您需要支持旧浏览器,只需使用基于回调的版本——尽管已弃用,但它在新的浏览器中仍然有效。查看浏览器兼容性表以了解更多信息。
示例
在我们的待办事项列表演示中,我们包含了一个“启用通知”按钮,当按下该按钮时,会请求应用程序的通知权限。
<button id="enable">Enable notifications</button>
单击此按钮将调用 askNotificationPermission()
函数
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()
函数中找到)
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()
之后仅为这些旧版本执行此操作,以防止从其他浏览器的通知托盘中删除通知。
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
实例上触发
可以使用 onclick
、onclose
、onerror
和 onshow
处理程序跟踪这些事件。因为 Notification
也是 EventTarget
的继承者,因此可以使用 addEventListener()
方法在其上。
替换现有通知
通常情况下,用户不希望在短时间内收到很多通知——例如,如果一个消息应用程序为每条传入的消息通知用户,而他们正在收到很多消息怎么办?为了避免向用户发送太多通知,可以修改待处理通知队列,用新的通知替换单个或多个待处理通知。
为此,可以为任何新的通知添加一个标签。如果通知已具有相同的标签,并且尚未显示,则新的通知将替换该之前的通知。如果具有相同标签的通知已经显示,则关闭之前的通知,并显示新的通知。
标签示例
假设以下基本的 HTML 代码
<button id="notify">Notify me!</button>
<section id="demo-logs"></section>
可以使用这种方式处理多个通知
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
网站。