通知 · API
Notification.requestPermission()
发布于 更新于
Notification.requestPermission() 询问用户是否允许该源显示系统通知,并以结果状态兑现;Notification.permission 随时读取该状态而不弹窗。同一个权限也把关 ServiceWorkerRegistration.showNotification(),并且因为推送订阅需要它,也把关 PushManager.subscribe(),所以这一个提示是 PWA 所有通知路径的闸门。
Notification.requestPermission()Notification.permissionrequestPermission() 是静态方法,返回 Promise<NotificationPermission>,字符串取值为 "default"、"granted" 或 "denied"。它还接受一个旧式回调参数 Notification.requestPermission(callback),这是 Safari 15 之前唯一支持的形式(BCD api.Notification.requestPermission_static)。Notification.permission 是只读静态属性,取值相同。两者都只存在于窗口中;worker 没有 Notification.requestPermission,Service Worker 通过 registration.pushManager.permissionState() 或 navigator.permissions.query({ name: 'notifications' }) 读取状态。
| 参数 | 类型 | 含义 |
|---|---|---|
deprecatedCallback |
function,可选 | 用户做出决定时以权限字符串调用。为兼容 Safari 7 至 14 保留;新代码直接等待 Promise。 |
三个权限值并不对称。"default" 表示用户尚未决定,浏览器按 "denied" 处理;"denied" 表示用户或企业策略已拒绝,直到用户更改站点设置之前不会再弹出提示;"granted" 是唯一能让 showNotification() 成功的状态。
requestPermission() 不会因拒绝而抛出,而是以 "denied" 兑现。下面的失败都发生在提示之前。
| 失败 | 位置 | 触发条件 |
|---|---|---|
ReferenceError |
读取 Notification |
iOS 与 iPadOS 上的 Safari 不定义该接口,除非页面作为 manifest 设置了非默认 display 的主屏幕 Web App 运行(BCD 对 api.Notification 的备注)。访问静态成员之前先测试 'Notification' in window。 |
不弹提示直接兑现 "denied",并打出控制台信息 |
Firefox 72+ | 调用不在用户产生的事件处理函数内。Firefox 记录 The Notification permission may only be requested from inside a short running user-generated event handler.(dom.properties 中的 NotificationsRequireUserGesture,github.com)。 |
不弹提示直接兑现 "denied",并打出控制台信息 |
Firefox 70+ | 调用来自跨源 <iframe>:The Notification permission may only be requested in a top-level document or same-origin iframe. |
不弹提示直接兑现 "denied",并打出控制台信息 |
Firefox | 页面不是安全上下文:The Notification permission may only be requested in a secure context. |
提示被抑制,状态停留在 "default" |
Chrome 80+ | Chrome 的静默权限界面用地址栏里一个带斜线的铃铛图标替代弹窗,针对习惯拒绝通知的用户和接受率低的站点(Introducing quieter permission UI for notifications,blog.chromium.org);Promise 只在用户点击图标并做出选择后才兑现。 |
macOS 上的 Safari 同样要求用户手势,否则不弹提示直接兑现 "denied"。
示例把「询问」(一次,来自手势)和「读取」(每次加载)分开,最后给出无法显示系统通知的平台的兜底。
解释原因后从按钮发起请求
Section titled “解释原因后从按钮发起请求”请求在 click 处理函数内运行,满足 Firefox 72 与 Safari 的要求。拒绝后按钮仍然可见,用户可以更改站点设置后再试。
const button = document.querySelector('#enable-alerts');
button.addEventListener('click', async () => { const state = await Notification.requestPermission(); if (state === 'granted') { button.hidden = true; await showWelcomeNotification(); } else { button.textContent = '通知已在浏览器设置中被拦截'; }});不要在加载时或定时器里调用 requestPermission():Firefox 会带着上面的控制台信息兑现 "denied",而 Chrome 和 Safari 会按站点记住这次拒绝。
不弹窗读取状态
Section titled “不弹窗读取状态”Notification.permission 是同步的且不会弹窗,所以它是决定是否渲染「启用」按钮的正确检查。Permissions API 以异步方式给出同样的答案,而且在 Service Worker 内也能用。
function notificationState() { if (!('Notification' in window)) return 'unsupported'; return Notification.permission; // "default" | "granted" | "denied"}
async function notificationStateInWorker() { const status = await navigator.permissions.query({ name: 'notifications' }); return status.state; // "prompt" | "granted" | "denied"}Permissions API 把未决定状态写作 "prompt",Notification.permission 写作 "default",两个词是同一个意思。
检测支持并退回页内提示
Section titled “检测支持并退回页内提示”iOS 浏览器标签页里没有该接口,Android WebView 里完全没有,而在任何平台上用户都可能已经拒绝。一个函数覆盖三种情况,返回一个应用其余部分可以调用的通知器。
async function getNotifier() { if (!('Notification' in window) || !('serviceWorker' in navigator)) { return showInPageToast; // 这里没有系统通知:在页面内渲染 } if (Notification.permission !== 'granted') { return showInPageToast; // 未决定或已拒绝:不要在这里弹提示 } const registration = await navigator.serviceWorker.getRegistration(); if (!registration?.active) return showInPageToast; return (title, body) => registration.showNotification(title, { body });}页内 toast 是每个分支的兜底,所以无论平台给出什么答案,调用方都只面对一个签名。
- Notifications API Standard: requestPermission()(whatwg.org)
- Notification: requestPermission() static method(developer.mozilla.org)
- Notifications API
- Web Push 与 PushManager.subscribe()
- iOS 与 iPadOS 上的 Web Push
规范
| 规范 | 状态 |
|---|---|
| Notification.permission / requestPermission()(通知权限) | WHATWG 现行标准 |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop) | 支持 | 32 | 中 | 来源 | — |
| Chrome (Android) | 支持 | 42 | 中 | 来源 | — |
| Edge (Desktop) | 支持 | 14 | 中 | 来源 | — |
| Firefox (Desktop) | 支持 | 22 | 中 | 来源 | — |
| Firefox (Android) | 支持 | 22 | 中 | 来源 | — |
| Safari (macOS) | 支持 | 7 | 中 | 来源 | — |
| Safari (iOS) | 部分支持 | 16.4 | 中 | 来源 | 1 |
| Samsung Internet | 支持 | 4.0 | 中 | 来源 | — |
| WebView (Android) | 不支持 | — | 中 | 来源 | 2 |
- 仅限已添加到主屏幕的 Web 应用;普通浏览器标签页不可用。
- 依据 MDN browser-compat-data,不支持。