Notifications e Permissions API
TL;DR
A Notifications API exibe notificações nativas do sistema operacional — úteis para alertas mesmo quando o usuário está em outra aba ou app. A Permissions API consulta e observa o estado de permissões do browser (câmera, microfone, notificações, geolocalização) sem triggar o prompt. Juntas, permitem criar fluxos onde você só pede permissão quando há valor real para o usuário, evitando o popup imediato ao entrar na página.
Permissions API — consultar sem pedir
A Permissions API permite verificar o estado atual de uma permissão sem disparar o prompt de solicitação:
// Consultar estado de uma permissão
const result = await navigator.permissions.query({ name: 'notifications' });
result.state;
// 'granted' — usuário concedeu
// 'denied' — usuário negou (prompt não vai aparecer novamente)
// 'prompt' — ainda não foi solicitado
// Observar mudanças de estado
result.addEventListener('change', () => {
console.log('Permissão de notificação mudou para:', result.state);
updateNotificationUI(result.state);
});Permissões consultáveis:
// Exemplos de permissões que podem ser consultadas
const names = [
'notifications',
'geolocation',
'camera',
'microphone',
'clipboard-read',
'clipboard-write',
'persistent-storage',
'push',
'screen-wake-lock',
];Notifications API
// 1. Verificar suporte
if (!('Notification' in window)) {
console.log('Este browser não suporta notificações');
return;
}
// 2. Pedir permissão (só funciona após gesto do usuário no Chrome)
const permission = await Notification.requestPermission();
// 'granted' | 'denied' | 'default'
// 3. Exibir notificação
if (permission === 'granted') {
const notification = new Notification('Nova mensagem', {
body: 'Você tem 3 mensagens não lidas',
icon: '/icons/message.png',
badge: '/icons/badge.png',
image: '/images/preview.jpg',
tag: 'mensagens', // agrupa notificações com a mesma tag
requireInteraction: false, // true = não fechar automaticamente
silent: false, // true = sem som
data: { url: '/messages' }, // dados customizados
actions: [ // botões (só em Service Worker)
{ action: 'reply', title: 'Responder', icon: '/icons/reply.png' },
{ action: 'dismiss', title: 'Descartar' },
],
});
notification.addEventListener('click', (event) => {
window.focus();
window.location.href = notification.data.url;
notification.close();
});
notification.addEventListener('close', () => {
console.log('Notificação fechada');
});
// Fechar programaticamente após 5s
setTimeout(() => notification.close(), 5000);
}Restrições do Chrome
Notification.requestPermission()requer gesto do usuário no Chrome — não pode ser chamado ao carregar a página. Sempre peça permissão em resposta a uma ação (clique num botão “Ativar notificações”).
Fluxo de UX correto para permissões
class NotificationManager {
// Não pedir permissão ao carregar — verificar estado primeiro
async init() {
const { state } = await navigator.permissions.query({ name: 'notifications' });
this.updateUI(state);
// Observar mudanças (usuário pode mudar nas configurações do browser)
const permResult = await navigator.permissions.query({ name: 'notifications' });
permResult.onchange = () => this.updateUI(permResult.state);
}
updateUI(state) {
const btn = document.getElementById('notification-btn');
if (state === 'granted') {
btn.textContent = 'Notificações ativadas ✓';
btn.disabled = true;
} else if (state === 'denied') {
btn.textContent = 'Notificações bloqueadas (mude nas configurações)';
btn.disabled = true;
} else {
// 'prompt' — usuário ainda não decidiu
btn.textContent = 'Ativar notificações';
btn.disabled = false;
btn.onclick = () => this.requestPermission();
}
}
async requestPermission() {
const permission = await Notification.requestPermission();
this.updateUI(permission);
if (permission === 'granted') {
this.showWelcomeNotification();
}
}
showWelcomeNotification() {
new Notification('Notificações ativadas!', {
body: 'Você receberá alertas importantes aqui.',
icon: '/icons/check.png',
tag: 'welcome',
});
}
// Mostrar notificação (verificar permissão antes)
notify(title, options = {}) {
if (Notification.permission !== 'granted') return;
return new Notification(title, options);
}
}Notificações via Service Worker
Para notificações Push (mesmo com a aba fechada), é necessário usar Service Worker:
// main.js — registrar o Service Worker
const registration = await navigator.serviceWorker.register('/sw.js');
await navigator.serviceWorker.ready;
// Exibir notificação via Service Worker
// (mais recursos: actions, vibration, persistent)
await registration.showNotification('Novo pedido!', {
body: 'Seu pedido #1234 foi confirmado',
icon: '/icons/order.png',
badge: '/icons/badge.png',
tag: 'order-1234',
data: { orderId: '1234' },
actions: [
{ action: 'view', title: 'Ver pedido' },
{ action: 'dismiss', title: 'Ok' },
],
vibrate: [200, 100, 200], // padrão de vibração (mobile)
});// sw.js — reagir ao clique na notificação
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.action === 'view') {
event.waitUntil(
clients.openWindow(`/pedidos/${event.notification.data.orderId}`)
);
}
});Screen Wake Lock — manter tela ligada
let wakeLock = null;
async function requestWakeLock() {
const { state } = await navigator.permissions.query({ name: 'screen-wake-lock' });
try {
wakeLock = await navigator.wakeLock.request('screen');
wakeLock.addEventListener('release', () => {
console.log('Wake lock liberado');
});
console.log('Tela não vai desligar enquanto você estiver lendo');
} catch (err) {
console.error('Wake lock negado:', err);
}
}
// Wake lock é liberado automaticamente quando a aba fica inativa
// Reativar quando o usuário volta
document.addEventListener('visibilitychange', async () => {
if (document.visibilityState === 'visible' && wakeLock !== null) {
wakeLock = await navigator.wakeLock.request('screen');
}
});
// Liberar manualmente
async function releaseWakeLock() {
await wakeLock?.release();
wakeLock = null;
}Para fixar
- Por que usar
navigator.permissions.query()antes deNotification.requestPermission()?- O que acontece se você chamar
Notification.requestPermission()programaticamente ao carregar a página no Chrome?- Qual a diferença entre
new Notification()eregistration.showNotification()? Quando usar cada um?- O que o campo
tagfaz em uma notificação?- Por que o wake lock é liberado automaticamente quando a aba fica inativa? Como você lidaria com isso?
Veja também
- 04 — Clipboard e File API — anterior
- 06 — Geolocation e Device APIs — próxima
- Workers 03 — Service Workers — context de push notifications