Quanto sarebbe comodo avere notifiche nei browser come in una normale app mobile?
Beh, ce le abbiamo già!
Ci sono solo alcuni concetti da padroneggiare prima di iniziare:
Per prima cosa dobbiamo verificare se il browser supporta Notification e serviceWorker.
Senza queste funzionalità non possiamo gestire le notifiche!
Basta scrivere:
if (!("serviceWorker" in navigator)) { throw new Error("No Service Worker support"); } if (!("Notification" in window)) { throw new Error("No Notification support"); }
NB: vale per qualsiasi feature del browser.
Per attivare le notifiche su uno specifico dispositivo, bisogna chiedere all'utente se vuole riceverle.
Notification.requestPermission();
Comparirà un popup nel browser che chiede la risposta dell'utente...

...e la nostra applicazione deve gestire la scelta così:
Notification.requestPermission().then((permission) => { if (permission === "granted") { // L'utente ha cliccato su "Allow" // Il browser PUÒ inviare notifiche } else if (permission === "denied") { // L'utente ha cliccato su "Block" // Il browser NON PUÒ inviare notifiche } else if (permission === "default") { // L'utente ha chiuso il popup // possiamo chiedere i permessi di nuovo se vogliamo! } });
Ok, ora potremmo essere pronti a inviare la prima notifica, ma prima parliamo di Service Worker!
Secondo MDN, i Service Worker agiscono essenzialmente come proxy server tra web app, browser e rete (quando disponibile).
In pratica, un ServiceWorker è un file JavaScript che gira in background su un thread separato.
Deve prima essere registrato (installato), poi può comunicare con l'app (NB: https è obbligatorio!).
Perché ci serve un ServiceWorker per inviare notifiche?
Perché gira in background anche quando la pagina dell'app non è aperta nel browser!
E soprattutto perché ServiceWorkerRegistration ha un metodo showNotification usato per mostrare la notifica sul dispositivo.
Dobbiamo solo aspettare che il service worker sia pronto e chiamare il metodo passando il titolo della notifica come primo parametro.
navigator.serviceWorker.ready.then((swRegistration) => { swRegistration.showNotification("My First notification!"); });
Queste poche righe mostreranno la notifica:

Il metodo showNotification ha un secondo argomento che accetta un oggetto di opzioni per personalizzare la notifica:
icon: URL di un'immagine mostrata accanto al titolo della notificaimage: URL di un'immagine mostrata come contenuto della notificaactions: array di azioni che diventano una lista di pulsanti cliccabili sotto al contenuto della notificaaction: stringa univoca che rappresenta l'ID dell'azionetitle: stringa human readable da visualizzareicon: URL dell'immagine accanto al titolobody: stringa con contenuto aggiuntivo da mostrare nella notificabadge: stringa con URL di un'immagine che rappresenta la notifica quando non c'è spazio sufficiente per mostrarla per intero (ad esempio nella barra notifiche Android)data: dati arbitrari che vuoi associare alla notificatag: ID della notifica che consente di trovarla, sostituirla o rimuoverla via script quando necessariosilent: se true, niente vibrazione o suonovibrate: array di numeri che rappresenta il pattern di vibrazioneProviamo a creare insieme una notifica "come back" che compare quando l'app perde il focus e si chiude automaticamente quando torna in focus. Inoltre, se cliccata, riporta l'utente all'app.
document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") { navigator.serviceWorker.ready.then(async (registration) => { await registration.showNotification("Come baaaaack!", { body: `Click here and come back to the website!`, silent: true, tag: "come-back", // richiesto se silent è true }); registration.addEventListener("click", (e) => { e.preventDefault(); window.parent.focus(); }); }); } else { navigator.serviceWorker.ready.then((registration) => { registration .getNotifications({ tag: "come-back", }) .then((notifications) => { notifications.forEach((n) => { n.close(); }); }); }); } });
"Come?! Hai appena detto che i ServiceWorker sono necessari per inviare notifiche!!"
Lo so... in realtà esiste un metodo alternativo che non coinvolge ServiceWorker, ma si basa su una feature deprecata!
Il costruttore window.Notification non è segnato come deprecato in generale, ma è deprecato in Chrome su Android, quindi non funzionerà sui dispositivi mobile.
Per questo ho preferito usare direttamente l'approccio con ServiceWorker.
Ti lascio comunque un esempio, può essere utile a chi non ha bisogno del supporto mobile.
Ricreiamo la notifica "come back" usando il costruttore Notification.
let comeBackNotification; document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") { comeBackNotification = new Notification("Come baaaaack!"); comeBackNotification.addEventListener("close", (e) => { console.log("Notification CLOSED!", e.target.data); }); comeBackNotification.addEventListener("click", (e) => { console.log("Notification CLICKED!", e.target.data); e.preventDefault(); window.parent.focus(); }); } else { comeBackNotification.close(); } });
notification.vibrate non funziona su Android > 8.0sw.js, se non ci serve, può anche essere un file vuoto!Una cosa importante sui service worker è che hai poco controllo su quando il loro codice verrà eseguito.
È il browser a decidere quando avviarli e quando terminarli.
L'unico modo per dire al browser di aspettare è passare una promise a event.waitUntil().
Così il browser terrà il service worker attivo finché la promise non sarà risolta o rifiutata.
// file: `sw,js` self.addEventListener("push", function (event) { const promiseChain = self.registration.showNotification("Push Notification"); event.waitUntil(promiseChain); });
La differenza essenziale tra notifiche locali e push notifications è semplice:
Notifiche locali: pianificate localmente dall'app e consegnate dallo stesso dispositivo.
Push notifications: inviate da un server remoto che le recapita ai dispositivi su cui è installata l'app.
Non parlerò della parte server, quindi spiego come simulare una Push Notification con Chrome DevTools.
ApplicationService Workers a sinistraPush
Come sempre, ho creato una demo e un progetto GitHub.
Durante l'uso della demo, controlla la console per maggiori informazioni.