Web Notifications API: Chrome, Firefox, and The Spec

posted in: Uncategorized | 1

Introduction

In this article, I am going to look at some of the challenges of using the Web Notification API and how one might start to code a cross-browser notification class.

What is a Web Notification?

From the W3 Spec:

“…an API to display notifications to alert users outside the context of a web page”

In short, it displays a little popup box on the bottom right of the user’s screen with a title, a message, and an icon.

Support

Chrome has supported it for quite a while (prefixed since 5.0) and Firefox just added it in version 22. According to caniuse, Opera 15 (Blink engine) supports it, and Safari has webkit prefixed support. IE? Don’t even bother.

What does the Spec say?

According to the spec, a Notification should be called as follows:

var notification = new Notification(title, options);

Firefox follows this spec quite nicely. Showing a basic box with a title, icon, and a body message:

var notification = new Notification(title, { icon: icon, body: message });

In Firefox, that line of code will show a notification, and after a short period of time, close it.

Chrome? WAT

In Chrome, things are different. Instead of the Notification object, chrome uses the webkitNotifications object.

var notification = webkitNotifications.createNotification(icon, title, message);

so, basically, everything is different. Completely different.

Anyways, calling this code in chrome should show a notification right? WRONG.

In chrome you need to call the .show() method.

var notification = webkitNotifications.createNotification(icon, title, message);
notification.show();

Unless Firefox, chrome won’t close the notification automatically. It will stay there until the user closes it.

Can I has Notifications?

In order to use notifications, you need to ask for permission.

In chrome:

webkitNotifications.requestPermission();

In Firefox:

Notification.requestPermission();

Both functions accept a callback. In Firefox, the callback function returns the result of the permission in a parameter of the callback function. Chrome, however, does not. So, you must use the following to check what the result was:

webkitNotifications.checkPermission()

Now, the spec and chrome disagree (as per usual on this topic) on what the permissions values should look like…

Spec:
default – notifications are not granted and have not been requested
granted – notifications are granted and have been requested
denied – notifications are not granted and have been requested

Chrome:
PERMISSION_NOT_ALLOWED = 1
PERMISSION_ALLOWED = 0
PERMISSION_DENIED = 2

Chrome WTF?

So, just because, in Chrome you can’t just… ask for permission. You may ONLY call the checkPermission() function IF and ONLY IF the code is being called as a result of some user interaction (example: click). I tried to “fake” a click event using elem.dispatchEvent(), but had no luck.

So what do we do? Well, you could add a button somewhere on your page, or you could dynamically create a div when you want to show a notification.

if(webkitNotifications.checkPermission() != 0){
	var permissionDiv = document.createElement("div");
	permissionDiv.className = "xNotification-permission";
	permissionDiv.innerHTML = "This site wants use desktop notifications. Please click on this bar to bring up the permission dialog.";
	document.getElementsByTagName("body")[0].appendChild(permissionDiv);
	permissionDiv.onclick = function(){
		document.getElementsByTagName("body")[0].removeChild(permissionDiv);
		webkitNotifications.requestPermission(function(){
			if(webkitNotifications.checkPermission() == 0){
				callback();
			} else {
				console.log("Not Allowed.");
			}
		});
	}
} else {
	callback();
}

If we don’t have permission, create a div. When the user clicks the div, remove it, and request permission. If we get permission, call the callback function. If we had permission to start with, it goes straight to the callback.

I used these styles for the div…

.xNotification-permission		{
	position:fixed;
	top:0px;
	left:0px;
	background:#e5e5e5;
	width: 100%;
	padding: 5px;
	cursor: pointer;
	border-bottom: 1px solid black;
}

xNotification Object

So, let’s create something that will be a little more consistent between Firefox (read: The Spec) and Chrome.

I want something that has this format:

var notification = new xNotification(title,message,icon);

I realize this doesn’t follow the spec completely, but this is just an example, not final code.

Sorry for the giant block of code here, but here is the class:

var xNotification = function(title,message,icon){
	
	function init(callback){
		if(window.webkitNotifications){
			if(webkitNotifications.checkPermission() != 0){
				var permissionDiv = document.createElement("div");
				permissionDiv.className = "xNotification-permission";
				permissionDiv.innerHTML = "This site wants use desktop notifications. Please click on this bar 

to bring up the permission dialog.";
				document.getElementsByTagName("body")[0].appendChild(permissionDiv);
				permissionDiv.onclick = function(){
					document.getElementsByTagName("body")[0].removeChild(permissionDiv);
					webkitNotifications.requestPermission(function(){
						if(webkitNotifications.checkPermission() == 0){
							callback();
						} else {
							console.log("Not Allowed.");
						}
					});
				}
			} else {
				callback();
			}
		} else if(window.Notification){
			Notification.requestPermission(function(permission){
				if(permission == 'granted'){
					callback();
				} else {
					console.log("Not Allowed.");
				}
			});
		}
	}
	
	init(function(){
		if(window.webkitNotifications){
			n = webkitNotifications.createNotification(icon, title, message);
			n.show();
		} else if(window.Notification){
			var notification = new Notification(title, { icon: icon, body: message });
		}
	});
	
	
};

So, when we create a new xNotification object, it is going to first call the init function. The init function, depending on if we’re in Chrome or Firefox (read: browser which uses the spec version), will attempt to get permission, and when it receives the go ahead, will call the callback function. The callback function that is passed back will show a notification depending on the implementation it should use (chrome or spec).

Events

According to the spec, the Notification API supports 4 events:

onclick – called when the user clicks on the event
onshow – called when the notification is shown to the user
onerror – called if the web application does not have permission to create a notification
onclose – called when the notification is dismissed (Safari treats this a bit differently)

Chrome ALMOST supports these, except instead of onshow, it is called ondisplay.

Other Browsers/Legacy/IE

Something else that could be added to this class is a “fallback” for browsers that don’t support notifications. Perhaps, a div could be dynamically created and displayed at the bottom right of the screen in a fixed position.

Conclusion

The Web Notification API just won a huge victory with Firefox adding support, but full cross-browser support is a bit off. Firefox (and somewhat Safari) are the only browsers that support the API properly, while Chrome does it’s own thing and IE has no idea.

However, using the code I presented as a starting point, one could start using the API today, using appropriate fallbacks.

Sources:

http://www.w3.org/TR/notifications
http://caniuse.com/notifications
http://www.darianshimy.com/blog/2012/08/14/web-notifications-in-chrome-and-safari/
http://www.html5rocks.com/en/tutorials/notifications/quick/

Special Thanks

Special thanks to Christian Heilmann (codepo8) and Šime Vidas (‏@simevidas) for answering my silly questions. 🙂

https://twitter.com/codepo8/status/349939521300873216

My name is Andrew McGivery. I currently work full time as an application developer at Manulife Financial in Canada. My current passion is building and leading highly engaged teams where employee happiness, learning, and growth is a priority.

One Response

  1. Thanks for listing the differences and considerations with chrome, I broke my head to ask permission without a click, preferably do it with the div

Leave a Reply