pin_drop当前位置:知识文库 ❯ 图文
HTML5 API:HTML5 Notification API - 完整教程与代码示例
一、教程简介
Notification API 允许 Web 应用在浏览器之外向用户显示桌面通知,即使页面不在当前标签页或浏览器被最小化,通知也能正常显示。该 API 广泛应用于消息提醒、邮件通知、任务完成提示、系统告警等场景,是构建实时通知系统的核心接口。
二、核心概念
通知生命周期
代码示例
请求权限 → 创建通知 → 显示通知 → 用户交互 → 通知关闭
│ │ │ │ │
│ │ │ onclick auto/手动
│ │ │ onclose
│ │ onshow
│ new Notification()
Notification.requestPermission()权限状态
Notification 选项
代码示例
new Notification(title, {
dir: 'auto', // 文字方向:auto/ltr/rtl
lang: 'zh-CN', // 语言
body: '通知正文内容', // 正文
tag: 'unique-tag', // 标签(同标签通知会替换)
icon: '/icon.png', // 图标 URL
image: '/preview.jpg', // 大图(部分浏览器支持)
badge: '/badge.png', // 徽章图标
vibrate: [200, 100, 200], // 振动模式(移动端)
timestamp: Date.now(), // 时间戳
renotify: false, // 相同 tag 是否重新通知
silent: false, // 是否静音
requireInteraction: false, // 是否需要用户手动关闭
actions: [ // 操作按钮(Service Worker 通知)
{ action: 'reply', title: '回复' },
{ action: 'ignore', title: '忽略' }
]
});三、语法与用法
请求权限
代码示例
// 现代写法(返回 Promise)
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
console.log('通知权限已授予');
}
});
// 或使用 async/await
async function requestPermission() {
const permission = await Notification.requestPermission();
return permission === 'granted';
}创建通知
代码示例
if (Notification.permission === 'granted') {
const notification = new Notification('新消息', {
body: '你收到了一条新消息',
icon: '/icon.png',
tag: 'message-1'
});
}事件处理
代码示例
const notification = new Notification('点击通知', {
body: '点击此通知打开页面'
});
notification.onshow = function() {
console.log('通知已显示');
};
notification.onclick = function() {
window.focus(); // 聚焦窗口
window.open('https://example.com', '_blank');
notification.close();
};
notification.onclose = function() {
console.log('通知已关闭');
};
notification.onerror = function() {
console.error('通知错误');
};通知标签(tag)
代码示例
// 相同 tag 的通知会替换旧通知,避免通知堆积
new Notification('聊天消息', {
body: '张三: 你好!',
tag: 'chat-zhangsan'
});
// 新消息会替换上面的通知
new Notification('聊天消息', {
body: '张三: 在吗?',
tag: 'chat-zhangsan'
});四、代码示例
示例一:通知管理面板
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notification API - 通知管理</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f0f2f5;
min-height: 100vh;
padding: 30px 20px;
}
.container { max-width: 640px; margin: 0 auto; }
h1 { font-size: 24px; color: #1a1a2e; margin-bottom: 8px; }
.desc { color: #666; font-size: 14px; margin-bottom: 24px; }
.card {
background: #fff;
border-radius: 12px;
padding: 24px;
margin-bottom: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.card h2 { font-size: 16px; color: #333; margin-bottom: 16px; }
.permission-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border-radius: 10px;
margin-bottom: 16px;
}
.permission-bar.granted { background: #e6f4ea; }
.permission-bar.denied { background: #fce8e6; }
.permission-bar.default { background: #fff3cd; }
.permission-status { font-size: 14px; font-weight: 600; }
.permission-bar.granted .permission-status { color: #1e7e34; }
.permission-bar.denied .permission-status { color: #c62828; }
.permission-bar.default .permission-status { color: #856404; }
button {
padding: 8px 18px;
border: none;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary { background: #0984e3; color: #fff; }
.btn-primary:hover { background: #0770c2; }
.btn-success { background: #00b894; color: #fff; }
.btn-success:hover { background: #00a381; }
.btn-warning { background: #fdcb6e; color: #333; }
.btn-warning:hover { background: #e5b85c; }
.btn-danger { background: #e17055; color: #fff; }
.btn-danger:hover { background: #d35f48; }
.btn-group { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.form-group { margin-bottom: 14px; }
.form-group label {
display: block;
font-size: 13px;
color: #666;
margin-bottom: 6px;
font-weight: 500;
}
input, select, textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
outline: none;
}
input:focus, select:focus, textarea:focus { border-color: #0984e3; }
textarea { resize: vertical; min-height: 60px; }
.notification-log {
max-height: 300px;
overflow-y: auto;
}
.log-item {
display: flex;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #f0f0f0;
font-size: 13px;
}
.log-item:last-child { border-bottom: none; }
.log-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
font-size: 14px;
flex-shrink: 0;
}
.log-icon.info { background: #e8f0fe; color: #4285f4; }
.log-icon.success { background: #e6f4ea; color: #34a853; }
.log-icon.warning { background: #fef7e0; color: #f9ab00; }
.log-icon.error { background: #fce8e6; color: #ea4335; }
.log-info { flex: 1; }
.log-title { font-weight: 600; color: #333; }
.log-body { color: #666; font-size: 12px; margin-top: 2px; }
.log-time { color: #999; font-size: 11px; }
.quick-tpl {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.tpl-btn {
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background: #fff;
cursor: pointer;
transition: all 0.2s;
text-align: left;
}
.tpl-btn:hover { border-color: #0984e3; background: #f0f7ff; }
.tpl-title { font-size: 13px; font-weight: 600; color: #333; }
.tpl-desc { font-size: 11px; color: #999; margin-top: 4px; }
</style>
</head>
<body>
<div class="container">
<h1>浏览器通知管理</h1>
<p class="desc">体验 Notification API,发送自定义桌面通知。</p>
<div class="permission-bar default" id="permBar">
<span class="permission-status" id="permStatus">通知权限:未请求</span>
<button class="btn-primary" id="permBtn" onclick="requestPermission()">请求权限</button>
</div>
<div class="card">
<h2>快速通知模板</h2>
<div class="quick-tpl">
<div class="tpl-btn" onclick="sendTemplate('message')">
<div class="tpl-title">新消息</div>
<div class="tpl-desc">模拟收到新消息通知</div>
</div>
<div class="tpl-btn" onclick="sendTemplate('success')">
<div class="tpl-title">操作成功</div>
<div class="tpl-desc">任务完成通知</div>
</div>
<div class="tpl-btn" onclick="sendTemplate('warning')">
<div class="tpl-title">系统警告</div>
<div class="tpl-desc">资源告警通知</div>
</div>
<div class="tpl-btn" onclick="sendTemplate('reminder')">
<div class="tpl-title">日程提醒</div>
<div class="tpl-desc">定时提醒通知</div>
</div>
</div>
</div>
<div class="card">
<h2>自定义通知</h2>
<div class="form-group">
<label>通知标题</label>
<input type="text" id="notifTitle" value="自定义通知" placeholder="输入标题">
</div>
<div class="form-group">
<label>通知内容</label>
<textarea id="notifBody" placeholder="输入通知内容">这是一条自定义的浏览器通知</textarea>
</div>
<div class="form-group">
<label>通知标签(同标签会替换)</label>
<input type="text" id="notifTag" placeholder="可选,留空则不设置">
</div>
<div class="btn-group">
<button class="btn-success" onclick="sendCustom()">发送通知</button>
<button class="btn-warning" onclick="sendWithDelay()">5秒后发送</button>
</div>
</div>
<div class="card">
<h2>通知历史</h2>
<div class="notification-log" id="notifLog">
<div style="text-align:center;color:#999;padding:20px;font-size:13px;">暂无通知记录</div>
</div>
</div>
</div>
<script>
const templates = {
message: { title: '新消息', body: '张三向你发送了一条消息:项目进展如何?', icon: '', tag: 'chat-message' },
success: { title: '操作成功', body: '文件已成功上传至服务器,共处理 156 个文件。', icon: '', tag: 'task-complete' },
warning: { title: '系统警告', body: 'CPU 使用率已达到 92%,请检查后台进程。', icon: '', tag: 'system-warning' },
reminder: { title: '日程提醒', body: '15:00 产品评审会议即将开始,请准时参加。', icon: '', tag: 'calendar-reminder' }
};
let notifCount = 0;
function updatePermissionUI() {
const permission = Notification.permission;
const bar = document.getElementById('permBar');
const status = document.getElementById('permStatus');
const btn = document.getElementById('permBtn');
bar.className = 'permission-bar ' + permission;
const labels = { granted: '已授权', denied: '已拒绝', default: '未请求' };
status.textContent = '通知权限:' + labels[permission];
if (permission === 'granted') { btn.textContent = '已授权'; btn.disabled = true; btn.style.opacity = '0.5'; }
else if (permission === 'denied') { btn.textContent = '已拒绝'; btn.disabled = true; btn.style.opacity = '0.5'; }
else { btn.textContent = '请求权限'; btn.disabled = false; btn.style.opacity = '1'; }
}
async function requestPermission() {
const permission = await Notification.requestPermission();
updatePermissionUI();
if (permission !== 'granted') { alert('通知权限被拒绝,请在浏览器设置中手动开启。'); }
}
function sendNotification(title, options) {
if (!('Notification' in window)) { alert('您的浏览器不支持 Notification API'); return; }
if (Notification.permission !== 'granted') { alert('请先授予通知权限'); return; }
try {
const notification = new Notification(title, options);
notification.onclick = function () { window.focus(); notification.close(); };
addLog(title, options.body || '', options.tag || '');
notifCount++;
setTimeout(() => notification.close(), 5000);
} catch (e) { console.error('通知发送失败:', e); }
}
function sendTemplate(type) { const tpl = templates[type]; sendNotification(tpl.title, { body: tpl.body, tag: tpl.tag }); }
function sendCustom() {
const title = document.getElementById('notifTitle').value.trim();
const body = document.getElementById('notifBody').value.trim();
const tag = document.getElementById('notifTag').value.trim();
if (!title) { alert('请输入通知标题'); return; }
const options = { body: body };
if (tag) options.tag = tag;
sendNotification(title, options);
}
function sendWithDelay() {
const title = document.getElementById('notifTitle').value.trim() || '延迟通知';
const body = document.getElementById('notifBody').value.trim() || '这是一条延迟5秒发送的通知';
const tag = document.getElementById('notifTag').value.trim();
setTimeout(function () {
const options = { body: body };
if (tag) options.tag = tag;
sendNotification(title, options);
}, 5000);
alert('通知将在5秒后发送,请切换到其他标签页或最小化窗口查看效果');
}
function addLog(title, body, tag) {
const log = document.getElementById('notifLog');
if (notifCount === 0) log.innerHTML = '';
const time = new Date().toLocaleTimeString('zh-CN');
const item = document.createElement('div');
item.className = 'log-item';
item.innerHTML = '<div class="log-icon info">🔔</div><div class="log-info"><div class="log-title">' + escapeHtml(title) + '</div><div class="log-body">' + escapeHtml(body) + (tag ? ' (tag: ' + escapeHtml(tag) + ')' : '') + '</div></div><span class="log-time">' + time + '</span>';
log.insertBefore(item, log.firstChild);
}
function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; }
updatePermissionUI();
</script>
</body>
</html>五、浏览器兼容性
六、注意事项与最佳实践
1. 权限请求时机
代码示例
// 不推荐:页面加载时立即请求
window.addEventListener('load', () => Notification.requestPermission());
// 推荐:用户操作后请求
button.addEventListener('click', () => {
if (Notification.permission === 'default') {
Notification.requestPermission();
}
});2. 通知频率控制
代码示例
// 避免短时间内发送大量通知
class NotificationThrottler {
constructor(minInterval = 5000) {
this.minInterval = minInterval;
this.lastSent = 0;
this.queue = [];
}
send(title, options) {
const now = Date.now();
if (now - this.lastSent >= this.minInterval) {
new Notification(title, options);
this.lastSent = now;
} else {
this.queue.push({ title, options });
}
}
}3. 使用 tag 避免通知堆积
代码示例
// 同一对话的消息使用相同 tag,新消息替换旧通知
new Notification('聊天消息', {
body: '最新消息内容',
tag: 'chat-user-123'
});4. 降级方案
代码示例
function showNotification(title, options) {
if (!('Notification' in window)) {
// 降级:页面内通知
showInPageNotification(title, options.body);
return;
}
if (Notification.permission === 'granted') {
new Notification(title, options);
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
new Notification(title, options);
}
});
}
}七、代码规范示例
代码示例
// 推荐:封装通知管理类
class NotificationManager {
constructor() {
this.supported = 'Notification' in window;
this.throttleMap = new Map();
}
get permission() {
return this.supported ? Notification.permission : 'denied';
}
async requestPermission() {
if (!this.supported) return false;
if (this.permission === 'granted') return true;
if (this.permission === 'denied') return false;
const result = await Notification.requestPermission();
return result === 'granted';
}
async send(title, options = {}) {
if (!this.supported) {
console.warn('Notification API 不可用');
return null;
}
if (this.permission !== 'granted') {
const granted = await this.requestPermission();
if (!granted) return null;
}
try {
const notification = new Notification(title, {
icon: options.icon || '/favicon.ico',
badge: options.badge,
body: options.body || '',
tag: options.tag,
requireInteraction: options.requireInteraction || false,
silent: options.silent || false,
...options
});
if (options.onclick) notification.onclick = options.onclick;
if (options.onclose) notification.onclose = options.onclose;
if (options.autoClose !== false) {
setTimeout(() => notification.close(), options.closeTimeout || 5000);
}
return notification;
} catch (e) {
console.error('通知发送失败:', e);
return null;
}
}
// 节流发送
throttledSend(key, title, options, minInterval = 5000) {
const now = Date.now();
const lastSent = this.throttleMap.get(key) || 0;
if (now - lastSent < minInterval) return null;
this.throttleMap.set(key, now);
return this.send(title, options);
}
}
// 使用示例
const notify = new NotificationManager();
async function init() {
const granted = await notify.requestPermission();
if (granted) {
notify.send('欢迎回来', { body: '你有3条未读消息', tag: 'welcome' });
}
}八、常见问题与解决方案
常见问题
通知权限被拒绝后如何重新获取?
一旦用户拒绝,无法通过代码再次请求。只能引导用户手动在浏览器设置中修改。
iOS Safari 不显示通知?
iOS 16.4+ 支持通知,但需要将 Web App 添加到主屏幕,且使用 Service Worker 注册通知。
通知的图标不显示?
图标 URL 必须是绝对路径,且需要同源或支持 CORS。解决方案:使用完整的 HTTPS URL 作为图标路径。
如何实现定时通知?
使用 setTimeout 延迟创建通知即可实现定时通知功能。例如:setTimeout(() => new Notification(title, options), 5 * 60 * 1000) 可以在5分钟后发送通知。
九、总结
Notification API 提供了在浏览器之外向用户显示桌面通知的能力,适用于消息提醒、任务完成通知、系统告警等场景。使用时需注意权限管理(在用户操作后请求)、通知频率控制(使用 tag 避免堆积)、降级方案(不支持时使用页面内通知)和自动关闭机制。通过封装通知管理类可以实现权限检查、节流发送、自动关闭等高级功能,使通知系统更加健壮和用户友好。
标签:
浏览器通知
Notification API
桌面通知
权限管理
消息提醒
HTML5 API
本文涉及AI创作
内容由AI创作,请仔细甄别