Notifications API 允许 Web 应用程序在系统级别发送在页面外部显示的通知,这样即使浏览器应用处于空闲或在后台运行,Web 应用程序也可以向用户发送信息,是与用户互动的一种非常强大的方式。并且在操作系统的通知管理器中会一直保存消息,用户可以在消息通知显示后再次查看。操作系统的通知系统当然会因平台和浏览器而异,但无需担心,Notifications API 被编写为通用的,足以与大多数操作系统的通知系统兼容。
Notifications API 简介
Notifications API 使用 Notification() 构造函数创建 Notification 对象的新实例。方法如下:
// 兼容处理
let Notification = window.Notification || window.webkitNotifications
// 创建实例 new Notification(title, options)
new Notification(‘系统通知’, {
// 文字方向,auto / ltr / rtl
dir: ‘ltr’,
// 赋予通知一个ID,以便在必要的时候对通知进行刷新、替换或移除
tag: guid(),
// 一个图片的URL,将被用于显示通知的图标
icon: 'path/to/icon.png',
// 通知中额外显示的字符串
body: 'message',
// 是否一直保持有效
requireInteraction: true
})
静态属性
Notification.permission,用来表明用户是否允许当前域显示通知:
- granted: 用户已经明确的授予了显示通知的权限。
- denied: 用户已经明确的拒绝了显示通知的权限。
- default: 用户还未被询问是否授权; 这种情况下权限将视为 denied
实例属性
除 Notification.permission 静态属性外,每个 Notification 实例都有:title、dir、tag、icon、body 和 requireInteraction 这 6个常用的只读属性,各个属性的意义在创建 Notification 对象实例的 options 属性中已经用注释说明了,这里就不赘述了。
静态方法
Notification. requestPermission(),用于在当前页面向用户申请显示通知的权限。调用方法如下:
// 用户已经授权过了
if (Notification.permission === 'granted''){
// 弹出消息
} else {
Notification. requestPermission((permission)=>{
if(persmission === 'granted''){
// 弹出消息
}
})
}
申请用户授权的时候,浏览器会弹出确认消息提示。如下图:
用户的响应与应用程序一起存储,因此再次调用 Notification.permission 会返回用户的最后选择(granted 或者 denied)。一旦用户授予权限,应用程序就可以显示 Notification 的通知。
注意,这个方法只能被用户行为调用(比如:onclick
事件),并且不能被其他的方式调用。也就是是说无法通过脚本编程手动设置 Notification.permission = ‘granted’ 的方式强制用户允许授权,这是浏览器的一种安全限制措施。
实例方法
Notification.close(),以编程方式关闭通知实例。
事件处理程序
Notification API 提供了 4 个处理程序:
- Notification.onclick:click 事件的处理程序。每次用户点击通知时都会触发它。
- Notification.onclose:close 事件的处理程序。当用户关闭通知时触发。
- Notification.onerror:error 事件的处理程序。每次通知遇到错误时都会触发它。
- Notification.onshow:show 事件的处理程序。它在显示通知时触发。
使用 Notifications API 的注意事项
在了解了 Notification API 的属性和方法之后,会发现 Notification API 的接口很简洁,调用起来也很方便。但使用它也有一些需要注意的事项。
安全上下文的限制条件
在 MDN 介绍 API 的一开始,就用警告提示的方式告诉我们:
此功能仅在安全上下文(HTTPS)、某些或所有支持的浏览器中可用。
使用 Notifications API 需要调用 API 的站点启用了 HTTPS,否则无法使用 Notifications API。
处理重复的通知
某些情况下对于用户来说,显示大量通知是件令人痛苦的事情。但是实际应用中又必须为用户提示每一条消息。为了避免数以百计的不必要通知铺满用户的桌面,可能需要接管一个挂起消息的队列。因此,需要为新建的通知添加一个标记。
new Notification(title, {
// 设置 tid 标记
tag: tid
})
如果有一条通知也具有相同 tid 的标记,并且还没有被显示,那么这条新通知将会替换上一条通知。如果有一条通知具有一个相同的标记,并且已经显示出来了,那么上一条通知将会被关闭,新通知将会被显示出来。
Notification API 的兼容处理
并非所有浏览器都将 Notifications API 实现到同一级别,不同的操作系统可能不支持相同的通知功能。因此在使用时候尽量使用所有浏览器都支持的属性和方法。例如前文中的创建 Notification 实例,获取 Notification 对象就需要处理兼容问题,而创建实例的配置参数也尽量使用各个浏览器都支持的属性(具体的兼容说明参考后文的浏览器情况章节)。
另外,用户可以拒绝显示 Notification 通知的授权或者用户的浏览器不支持 Notifications API 的时候,需要使用其它的消息显示的替代方案。
浏览器支持情况
Notifications API 的浏览器支持情况(完整的 API 接口使用情况,请参考《MDN Notification 浏览器兼容性》)还是不错的,主流浏览器中除了 IE 不支持,其余的浏览器都支持。
实践
使用 Notifications API 对安全上下文限制条件,所以首先需要在网站启用 HTTPS。另外两个问题是如何处理重复消息的显示,以及在无法使用 Notifications API 的时候使用其它的方式展示消息通知。
消息通知分类
处理重复消息的方式是按功能模块给消息分类,为每个分类设置 tid。
let notify = new Notification(title, {
// tid 为分类的 id
tag: tid,
icon: iconPath,
body: msg
})
同一类的消息 tid 相同,这样在用户同时收到多个同类消息通知的时候,只会展示一条同一分类的消息,多个消息逐条显示,直到所有消息显示完毕。
如果给每条消息都设置不同 tid 反而不好,因为这会导致一次弹出很多个通知弹窗。
渐进增强的消息通知显示交互方式
但凡遇到有兼容性的问题时,都需要在设计时考虑渐进增强的交互设计方案。除了使用 Notifications API 显示消息通知外,同时也需要考虑在不支持的时候,采用了最常见的模拟消息弹窗的交互方式,以确保用户能查看到消息通知。
对 Notifications API 的封装
最后让我们来把 Notifications API 简单封装成 notofiy() 方法,方便在其他地方调用:
/**
* Notifications API 简单封装
* =====================================================================
* @param {String} title - 通知标题
* @param {String} msg - 通知正文
* @param {Object} [options] - 配置参数
* @returns {Notification}
*/
const notify = (title, msg, options) => {
const Notification = window.Notification || window.webkitNotifications
const defaults = {
// 文字方向,auto / ltr / rtl
dir: 'ltr',
// 赋予通知一个ID,以便在必要的时候对通知进行刷新、替换或移除
tag: new Date().getTime(),
// 一个图片的URL,将被用于显示通知的图标
icon: 'path/to/icon.png',
// 通知中额外显示的字符串
body: msg,
// 是否一直保持有效
requireInteraction: true
}
const config = Object.assign(defaults, options)
// 用户已经授权
if (Notification.permission === 'granted') {
return new Notification(title, config)
} else {
// 申请用户授权
Notification.requestPermission((permission) => {
// 用户同意授权
if (permission === 'granted') {
return new Notification(title, config)
}
})
}
}