HTML 5 has some great API’s one of which is the notifications api which lets you set browser notifications to the end user, outside of the website scope so they can be issued when the user switches to another tab or application.
Mozilla Developer Network describes Notifications API best:
The Notifications API allows web pages to control the display of system notifications to the end user — these are outside the top-level browsing context viewport, so therefore can be displayed even the user has switched tabs or moved to a different app. The API is designed to be compatible with existing notification systems across different platforms.
You should have seen got a popup when first landing on this page if not you may need to reload the page to initialize the permission request, the permission request will permission to show notifications, if accepted notifications can be displayed.
Notification API
The notifications API is JavaScript based. This js code will check if the browser supports the API and then check if permission has been given to run. If no permission request has ran a new request will run.
If granted set isGranted to true this will be used to run the notification next.
//default to false
var isGranted = false;
//check if the browser supports notifications
if (!("Notification" in window)) {
//console.log("This browser does not support desktop notification");
} else if (Notification.permission === "granted") {
//set to true
isGranted = true;
} else if (Notification.permission !== 'denied') {
//ask user for permission
Notification.requestPermission(function (permission) {
if (permission === "granted") {
//set to true
isGranted = true;
}
});
}
If granted is equal to true. This include the Vibration API to vibrate mobile devices, at the time of writing this IOS is not supported only Android. Pass the number of seconds to milliseconds vibrate.
Next create the notification, for convenience I’m using variables here so it can be wrapped in a function and passed data to it which is displayed further down. The parameters being used are the title, path to an icon and the message to display.
Next get the current document title and store it so it can be manipulated and changes back later. Update the document title as add (new message) before the title, this can be changed to anything you like.
use notification.onclick to watch for click events then direct to the url (again this is a convenience function passed to the function) close the notification window before going to the url.
When the notification window closed set the document title back to the original.
if(isGranted == true){
//use the vibrate api
window.navigator.vibrate(500);
//create a new notification
var notification = new Notification(title,{ icon: icon , body: body});
//get the document title and store it
var originalTitle = document.title;
//change document title
document.title = '(new message!) ' + originalTitle;
//when clicked close the notification and go to the url
notification.onclick = function () {
notification.close();
window.location.href = url;
};
//when closing the notification change the document title
notification.onclose = function () {
//change document title back to original
document.title = originalTitle;
};
}
To call the permissions request:
//request permission
Notification.requestPermission();
I’m wrapping this code inside a function for easy reuse:
function notifyMe(title, body, icon, url) {
Calling the function and passing the data
notifyMe('Hello!', "Hey it's working!", '/favicon.ico', '');
This all works really well but alerting users whilst on the website already could be off putting to the user, instead only show the notifications if the user is on another tab and not focusing on the website.
Page Visibility API
This page visibility api gives the ability to detect is the user is on the page or on another tab using an addEventListener check for when visibilitychange is fired, when it does call visibleChangeHandler this is a custom functon so you can name this anything you like.
document.addEventListener('visibilitychange', visibleChangeHandler, false);
Annoyingly getting this to work on different browsers requests different prefixed, I don’t know why they can’t all use document.hidden!
This snippets checks the different browser prefixed to determine when the page is active or not, then a simple if statement can be used if(document[hidde’]){
function visibleChangeHandler() {
var hidden, visibilityChange;
if (typeof document.hidden !== 'undefined') {
// Opera 12.10, Firefox >=18, Chrome >=31, IE11
hidden = 'hidden';
visibilityChangeEvent = 'visibilitychange';
} else if (typeof document.mozHidden !== 'undefined') {
// Older firefox
hidden = 'mozHidden';
visibilityChangeEvent = 'mozvisibilitychange';
} else if (typeof document.msHidden !== 'undefined') {
// IE10
hidden = 'msHidden';
visibilityChangeEvent = 'msvisibilitychange';
} else if (typeof document.webkitHidden !== 'undefined') {
// Chrome <31 and Android browser (4.4+ !)
hidden = 'webkitHidden';
visibilityChangeEvent = 'webkitvisibilitychange';
}
//check if document is visible
if (document[hidden]) {
console.log('Page is not visible');
} else {
console.log('document is not visible');
}
}
Having this code and open your browser inspector’s console tab move away from the page to another tab and back again the console will update.
Putting it all together
Now lets only show notifications if the user is on a different tab:
//when visibility changes update visibleChangeHandler function
document.addEventListener('visibilitychange', visibleChangeHandler, false);
function visibleChangeHandler() {
var hidden, visibilityChange;
if (typeof document.hidden !== 'undefined') {
// Opera 12.10, Firefox >=18, Chrome >=31, IE11
hidden = 'hidden';
visibilityChangeEvent = 'visibilitychange';
} else if (typeof document.mozHidden !== 'undefined') {
// Older firefox
hidden = 'mozHidden';
visibilityChangeEvent = 'mozvisibilitychange';
} else if (typeof document.msHidden !== 'undefined') {
// IE10
hidden = 'msHidden';
visibilityChangeEvent = 'msvisibilitychange';
} else if (typeof document.webkitHidden !== 'undefined') {
// Chrome <31 and Android browser (4.4+ !)
hidden = 'webkitHidden';
visibilityChangeEvent = 'webkitvisibilitychange';
}
//check if document is visible
if (document[hidden]) {
//Page is not visible
notifyMe('Hello!', "Hey it's working!", '/favicon.ico', '');
} else {
//document is not visible do nothing.
}
}
function notifyMe(title, body, icon, url) {
//default to false
var isGranted = false;
//check if the browser supports notifications
if (!("Notification" in window)) {
//console.log("This browser does not support desktop notification");
} else if (Notification.permission === "granted") {
//set to true
isGranted = true;
} else if (Notification.permission !== 'denied') {
//ask user for permission
Notification.requestPermission(function (permission) {
if (permission === "granted") {
//set to true
isGranted = true;
}
});
}
if(isGranted == true){
//use the vibrate api
window.navigator.vibrate(500);
//create a new notification
var notification = new Notification(title,{ icon: icon , body: body});
//get the document title and store it
var originalTitle = document.title;
//change document title
document.title = '(new message!) ' + originalTitle;
//when clicked close the notification and go to the url
notification.onclick = function () {
notification.close();
window.location.href = url;
};
//when closing the notification change the document title
notification.onclose = function () {
//change document title back to original
document.title = originalTitle;
};
}
//request permission
Notification.requestPermission();
}