Generated Content by David Storey

Web Notifications

One of the traditional advantages of native apps over the Web is that they can access the platform’s built in notification system. Mobile operating systems generally have their own baked in, while desktop OS like Mac OS X have commonly found 3rd party notification systems such as Growl.

In the near future, we should be able to take advantage of this kind of functionality in our web apps as well. The Web Notifications specification provides us with this ability. This defines what are known as simple notifications. With simple notifications it is possible to create a notification which contains an icon, title, and body test. This can then be displayed by the notification system that the browser/user-agent hooks into. The spec doesn’t define this, so the user-agent can either roll its own, or use an existing system made available by the operating system.

Current support

The support situation doesn’t look too good at present. There are two implementations, one in Chrome, and one in Firefox Mobile. There is no support in desktop Firefox, and no support in the Android browser. The two implementations are far from interoperable, and both diverge quite a bit from the spec. The spec divergence is assumably because the spec has changed since it was implemented. It is possible to write code to make notifications work in both browsers however, although it won’t follow the current spec. With some more work it may be possible to write a polyfill though.

Displaying a notification

Checking and asking for permission from the user

As notifications can be intrusive (imagine what advertising companies and spammers could do with it if they could just post notifications as they saw fit?) the first thing you need to do is request permission from the user. You can also check what the current permission level is, so that you can take appropriate action.

In the spec this is handed off to the Feature Permissions spec. This spec defines a common model that will be used by all features that have to ask the user for permission. You can ask for the current permission level using something like:

var level = navigator.permissionLevel("notifications");

The permissionLevel method accepts a string that identifies the feature (“notifications” in this case), and returns one of the permission values USER_ALLOWED, DEFAULT_ALLOWED, DEFAULT_DENIED or USER_DENIED. The default value before asking for permission is either DEFAULT_ALLOWED or DEFAULT_DENIED. It is up to the user-agent to define what the default value is. You could imagine that in a web browser on the Web it would be DEFAULT_DENIED (this is how both current implementations act), but with installable applications such as Widgets, permissions could be set up front during installation and default could be set to DEFAULT_ALLOWED.

If the permission level is DEFAULT_DENIED, you have to ask the user for permission. This is done with the requestPermission function. Like permissionLevel it accepts a feature string as an parameter. The requestPermission method is asynchronous so it also accepts a callback parameter that fires after the user has responded to the prompt to give or deny permission. This gives you a chance to respond to the users choice.

It is important to note that you can not just call requestPermission in script. It is required that it can only be called after a user gesture, such as when they have pressed a key, or clicked a button or link. You can do this with an onclick event handler function for example. This is presumably to stop malicious scripts from inundating the user with requests without the user taking any action.

This would all be well and good, but neither of the implementations currently work this way. In Firefox Mobile there is no way (that I could see) to request the permission level or to ask for permission. Instead, Firefox Mobile asks permission automatically when you try to create notification. Chrome on the other hand defines similar but different methods on the NotificationCenter interface–accessed via window.webkitNotifications–along with methods for creating the notifications (more on them later). If you understood the previous concepts then it is easy enough though. Lets look at a quick example using WebKit’s API:


var level = webkitNotifications.checkPermission();

document.getElementById("permission").addEventListener("click", function() {
    // if checkPermission returns 1 then permission needs to be requested
    if (level === 1) {
         webkitNotifications.requestPermission();
    }
}, false);

In the example above we first check the permission level. In a real example we would probably do something with this such as show, hide, or disable a button to request the users permission.

Next we add an event listener to an element, and when the click event fires we check if the permission level is 1 (PERMISSION_NOT_ALLOWED), and then request permission if so. A dialog should then show in Chrome asking to accept or deny permission. If the user accepts the permission value will change to 0 (PERMISSION_ALLOWED) and if they deny it will change to 2 (PERMISSION_DENIED). If the user denies access then we can not ask for permission again unless the user goes into settings and removes the block.

As you will be able to see, the basic concept is the same as the spec, but the access point and values are quite different.

Creating the notification

Once the user has given permission then we can create the notification. As is common with the support for this spec, there is three different ways: the spec, WebKit implementation, and Gecko implementation.

The spec defines that notifications are created as follows:


var notify = new Notification("avatar.png", "Notification title", "Notification body text");

In WebKit and Gecko we use the createNotification method on the same interface we used for checking for permission. The Gecko interface is named slightly differently, uses the moz prefix and can be found on the navigator object instead. It also reorders the parameters just to make life more interesting:


    var notify,
        iconURL = "http://a2.twimg.com/profile_images/301953578/Picture_4_normal.png",
        title = "You have a new notification",
        desc = "Isn\u2019t that fantastic?";

    // creating a notification in Gecko 
if (navigator.mozNotification) {
    notify = navigator.mozNotification.createNotification(title, desc, iconURL);
} else if (window.webkitNotifications) {
    // create same notification in WebKit.
    notify = webkitNotifications.createNotification(iconURL, title, desc);  
}</code>

Note that the URL for the icon is last in Gecko (it is also optional), and that WebKit use plural for webkitNotifications. If you don’t want to set one of the parameters you can always send an empty string. Once we’ve finished we end up with a notification object in all three versions.

Showing the notification

This is the easy part. All three use the show() method on the notification object!


notify.show();

After calling this we will see our notification on screen. Chrome rolls its own UI for notifications, which is shown at the top right of the screen by default. Firefox Mobile uses Growl on Mac and the native notifications draw on Android.

Responding to events

There are a number of things we can do once a notification is shown:

Reacting to showing the notification

When a notification is shown the show event is fired. Except in the WebKit implementation it is the display event. And the Gecko implementation doesn’t support this at all. We can handle the show/display event to respond in some way to the notification being shown:


notify.onshow  = notify.ondisplay = function() { setTimeout(function() { notify.cancel(); }, 3000); }
// after we call show(), the show event will fire and will be handled by the line above
notify.show();

In this example I’m using either an onshow or ondisplay event handler to set the timeout to 3 seconds from the moment the show (spec) or display (WebKit) events are fired. Once the timeout is over cancel() is called, which closes the notification (Gecko doesn’t support this either).

Reacting to errors

After calling show(), if there is an error that causes the notification not to show (the spec doesn’t define what these errors might be), the error event is fired. We can then handle the error gracefully. Except in Gecko, where errors are not supported. You’ll be glad to know that WebKit and the spec match in this case:


notify.onerror  = function() { 
    // oh no error! fallback to our favourite alert
    alert(title + "\n" + desc);
}
//show the alert and see if there are any errors
notify.show();

Reacting to clicking on the notification

If the user clicks (or touches, or otherwise interacts) with the notification, then the click event is fired. This is consistent across the spec, WebKit and Gecko, although it isn’t mentioned in the WebKit spec for some reason. It can be useful for things such as displaying the email mentioned in a new mail notification, or showing the event linked to a calendar event notification.


notify.onclick  = function() { 
    // go to email
    showEmail(msgId);
}
//show the alert
notify.show();

Reacting to closing the notification

When a notification is closed, such as by the user, by the system, or by calling cancel() the close event is fired. This is consistent across the spec, WebKit and Gecko:


//This will be called when the notification is shown, and will close it in 2 seconds
notify.onshow  = notify.ondisplay = function() { setTimeout(function() { notify.cancel(); }, 2000); }
//Once the notification is closed, it will annoy the user with an alert!
notify.onclose  = function() { 
    alert("You dismissed the notification, so I’m going to pop up!");
}
//show the notification 
notify.show();

Other options

There are a couple more options not covered above.

Notification direction

It is possible to set the direction of the content in the notification for left to right and right to left text. This is set using the dir attribute of the Notification interface. The default is auto which sets the directionality to the directionality of the Document object. You can set it manually using notify.dir = "rtl" (such as for Arabic or Hebrew text) or notify.dir="ltr" for example. This method is not mentioned by the WebKit spec but it is supported. It is not supported by Gecko.

Rich notifications using web content

It is also possible to create notifications that use web content such as HTML or SVG. This was taken out of the Web Notifications spec however, and placed in its own Web URL Notifications spec. I will not cover it in this article as the spec has not been edited in a year and still follows the conventions in the WebKit implementation (such as using the NotificationCenter interface) rather than the newer style found in the Web Notifications spec. I expect this will be updated some time in the future to match the newer spec.

Future implementations

I’m not aware of any plans to update Gecko or WebKit to the newest spec. The differences are currently quite big, so I would guess they are waiting for the spec to stabilise. Web Notifications are currently on PhoneGap’s roadmap for December of this year. If this makes it then it will be possible to take advantage of the native notification support on the platforms that PhoneGap supports.

Demo time

I’ve created a quick, simple demo which works in both Chrome and Firefox Mobile. It attempts to take care of some of the implementation inconsistencies with the spec, but not all. I still use the NotificationCenter to create notification objects and check for and set permissions. It is left as a task to the reader to create a proper polyfill.

In the demo you can click the authorise button to grant access if using WebKit. Gecko doesn’t need this so it will be disabled. Once the permissions are set you can click on the notify me button to show the notification. After three seconds it will automatically close. On a real site you should be careful of the accessibility considerations of showing and hiding notifications.

View the Web Notifications demo (Requires Chrome or Firefox Mobile (including the emulator)

View source to see exactly how it works. I’ve kept the styles and JavaScript in one file to make it easier to read.

  1. skinwrinkles2011 reblogged this from dstorey
  2. scar-removal-guide reblogged this from dstorey
  3. pinched-nerve-solution reblogged this from dstorey
  4. effects-of-diabetes reblogged this from dstorey
  5. inetgate reblogged this from dstorey
  6. dstorey posted this