Notification in Browsers
How nice would it be to have notifications in browsers like a regular mobile application?
Well, we already have it!
There are just a couple of concepts we need to master before start:
- Browser support
- Notifications permissions
- Service Workers
Does the browser support it?
First of all, we need to check if the browser supports the Notification
and the serviceworker
features. Without
these services we cannot manage notifications!
It is as easy as typing:
if (!('serviceWorker' in navigator)) { throw new Error('No Service Worker support') } if (!("Notification" in window)) { throw new Error('No Notification support') }
NB: This applies to any browser feature.
Notifications permissions
In order to activate notifications on a specific device, the user must be asked if they want to receive notifications.
Notification.requestPermission()
An alert will pop up in the browser asking the user to respond...
...and our application must handle the user's choice in this way:
Notification.requestPermission().then((permission) => { if (permission === "granted") { // The user clicked on "Allow" button // Browser CAN send notifications } else if (permission === "denied") { // The user clicked on "Block" button // Browser CANNOT send notifications } else if (permission === "default") { // The user closed the popup // we can ask for permissions again if we want! } });
Okay, now we may be ready to send our first notification to the user, but first let's talk about ServiceWorker!
ServiceWorker
According to MDN Service Workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available).
Practically speaking, a ServiceWorker
is a JavaScript file that runs in the background in a separate thread.
It must be registered (installed) first, and then it can talk with the application (NB: https
is mandatory!)
Why do we need a ServiceWorker to send notification? Because it runs in the background even if our application page is
not open in the browser! And, above all, because
the SerciveWorkerRegistration
has a
method called showNotification
which is used to displays the notification on the device.
Send a notification
We just have to wait until the service worker is ready and call the method by passing the notification title as the first parameter.
navigator.serviceWorker.ready.then((swRegistration) => { swRegistration.showNotification("My First notification!"); })
These few lines of code will show the notification:
Notification properties
The showNotification
method has a second argument that accepts an object of options which allows us to customize the notification:
icon
: an URL representing an image that will be displayed next to the notification titleimage
: an URL representing an image that will be displayed as a content of the notificationactions
: an array of actions that will result in a list of clickable buttons below the notification contentaction
: a unique string representing the ID of the actiontitle
: a human readable string to be displayedicon
: an URL representing the image next to the titlebody
: A string representing an extra content to display within the notification.badge
: a string containing the URL of an image to represent the notification when there is not enough space to display the notification itself such as for example, the Android Notification Bar.data
: Arbitrary data that you want to be associated with the notificationtag
: An ID for a given notification that allows you to find, replace, or remove the notification using a script if necessary.silent
: iftrue
, no vibration or alert soundvibrate
: an array of numbers representing the vibration patter- ...and many more!
"Come back" notification
Let's try creating together a "come back" notification that shows up when the application lose focus and hides automatically when it is on focus again. It also takes the user directly back to the application if the notification is clicked.
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", // required if silent is set to "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(); }) }); }) } });
Notifications without ServiceWorker
"What?! You just said that ServiewWorker are required to send notification!!"
I know... There is an alternative method which doesn't involve ServiceWorker
actually, but it relies on a deprecated
feature! The window.Notification
constructor is not marked as deprecated, however, it’s marked as deprecated in Chrome on Android, thus it won't work on
mobile devices! This is why I preferred to use the ServiceWorker method directly!
I'm going to write an example because it might be interesting for those who don't deal with mobile browsers!
"OLD" way: Notification constructor
Let's re-create the "come back" notification using the Notification
constructor.
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(); } })
Good to know
- Starting in Chrome 49, notifications don't work in incognito mode.
notification.vibrate
doesn't work in Android > 8.0sw.js
, if we don't need it, it could be just an empty file!- Chrome for Android requires the call to be made with a service worker registration
- iOS requires website to first be added to the Home Screen
Wait Until
One of the things to understand about service workers is that you have little control over when the service worker code
is going to run. The browser decides when to wake it up and when to terminate it. The only way you can tell the browser
to wait, is to pass a promise into the event.waitUntil()
method. With this, the browser will keep the service worker
running until the promise you passed in has settled.
// file: `sw,js` self.addEventListener('push', function(event) { const promiseChain = self.registration.showNotification("Push Notification"); event.waitUntil(promiseChain); })
Push Notifications (from remote server)
The essential difference between local notifications and push notifications is simple:
Local notifications are scheduled by an app locally and are delivered by the same device.
Push notifications are sent by a remote server which sends these notifications to devices on which the app is installed.
I won't talk about the server side part, thus I'm going to explain how to simulate a Push Notification using Chrome DevTools.
Simulate a Push Notification with Chrome DevTools:
- Open Chrome DevTools
- Go to
Application
tab - Select
Service Workers
on the left - Write a custom message on the right anche click
Push
Demo
As usual, I created a demo and a GitHub project
While running the demo, check the console
for more information.