Rey Bango

Web developer, honey badger

Site Pinning: Rotating Overlay Icons for Multiple Service Notifications

In my last post, I went over how to use IE9?s Site Pinning API to implement overlay icons to enhance user notifications. The demo focused on how to display a numeric icon to indicate when a specific event (e.g.: messages in an inbox) had occurred.


Pinned site with overlay icon

It’s a really great way of letting your users know that there’s pending information for them to check into. But what happens if your site offers multiple types of notifications? With websites offering so much functionality nowadays, it’s pretty common for them to also serve up multiple types of notifications, from friend requests and event reminders to new messages and game invites.

Rotating Multiple Overlays Icons

The great thing about the Site Pinning API is that it’s very flexible and through some JavaScript magic, you can easily display multiple overlay icons for the various services you have. In this demo, I want to rotate through 3 different overlay icons that alert the user to pending messages, requests and actions.

As before, I had to flex some of my artistic talent by creating the overlay icons using the x-icon editor. I created 5 of each and here’s how the first three look:

The code changed slightly from the last demo in order to accommodate multiple bits of data per fetch. While previously, I was only fetching one piece of data, in this demo, I’m returning 3, one for each notification type:

 myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
			   ]);

As a reminder, the method getData() simulates grabbing remote data. So if we look at the data above, we can simulate pulling back three distinct bits of data. This is why we call the method every 10 seconds using setInterval. This allows us to see how notifications might look over a period of time.

setInterval(function () { myPin.getData() }, 10000);

The next thing that changed is the use of a timer to allow a slight delay while rendering the overlay icons. Using setTimeout() provides enough of delay so that an individual overlay icon is visible to the user before rotating on to the next icon. If we didn’t have this delay, the rotation would be way too fast to provide any useful notification. If we look at the following image, we can see what the notification will look like:


Overlay icon showing numeric notification

This is accomplished via the following code:

// Grab the current set of data...
currData = this.dataBin[this.currIndex++].data;		
		
/* We're going to display a new overlay every x number of seconds to display a new overlay icon so
   let's loop through the data elements for the current set of data... */
for (var i=0; i < currData.length; i++ ){
					
	(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1000 * idx); }( i ));					
					
}

Here’s what’s happening. In the first line, I grab the current set of data that holds all of the notification information (messages, requests & actions). That data looks like this:

[{ "label" : "Messages", "ntype" : "M", "num": 2 }, 
{ "label" : "Requests", "ntype" : "R", "num": 1 }, 
{ "label" : "Actions", "ntype" : "A", "num": 3 }]

I loop through each group of data and assign a timer using setTimeout() that will call dispOverlay() at ~1 second intervals. That’s the magic code that allows for the gradual icon rendering delay I mentioned before. The expected functionality is that the “messages” icon will render followed by the “requests” icon 1 second later, and then finally the “actions” icon.

Now, you might be wondering why I have an anonymous function wrapping the setTimeout(). It’s because I have a closure within setTimeout which can cause a common scoping issue in which the variable ‘i’, which I use to grab the current index of data, will only be updated to the last index value. James Padolsey has a great explanation on it and thanks to John David Dalton for helping me troubleshoot this.

The final change is in dispOverlay() in which I need to determine which overlay icon needs to display. Since I now have three different types of notifications, I need a conditional statement to determine the type and build the correct icon name:

if (theData.ntype == "M") {
	oImg = "images/messages-" + theData.num + ".ico";
} else if (theData.ntype == "R") {
	oImg = "images/requests-" + theData.num + ".ico";
} else if (theData.ntype == "A") {
	oImg = "images/actions-" + theData.num + ".ico";
}

This checks the type and serves up the right icon based on the type and the number of notifications pending for that type.

The Demo and Final Code

You can check out the demo by going here in IE9:

http://reybango.com/demos/sprotate/index.html

When the page renders, drag the tab down to your taskbar and pin it. You should see a new window appear with your newly pinned site. Next, you’ll see the overlay icons appear in the taskbar and they should begin to cycle every 10 seconds.

Here’s the full source code. You can also download everything here.

<!DOCTYPE html>
<html>
<head>
<title>Pinned Site - Rotating Overlay Icons</title>
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />
<meta name="application-name" content="Pinned Site Test" />
<meta name="msapplication-starturl" content="http://reybango.com/demos/sprotate/index.html" />
<meta name="msapplication-navbutton-color" content="#3480C0" />
<meta name="msapplication-window" content="width=1024;height=768" />
<meta name="msapplication-tooltip" content="Testing the Pinned Site API" />
<style>
body {
    background: none repeat scroll 0 0 #4492CE;
    font: 440%/1.4em 'Segoe Light',Segoe,'Segoe UI','Meiryo Regular','Meiryo',sans-serif;	
    color: #EDEFF4;
}

</style>

</head>

<body>

<div>
<h1>Pinned Sites</h1>
<p>Rotating Overlay Icons</p>
</div>

<script>

	var myData = [];

    var myPin = {

        currIndex: 0,
        dataBin: [],
		
        getData: function () {

			var idx = 0, currData = [], cntr = 0, theData;
		
            // Determines whether the current page was launched as a pinned site...
            if (window.external.msIsSiteMode()) {

				// Grab the current set of data...
				currData = this.dataBin[this.currIndex++].data;		
		
				/* We're going to display a new overlay every x number of seconds to display a new overlay icon so
				   let's loop through the data elements for the current set of data... */
				for (var i=0; i < currData.length; i++ ){
					
					(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1e3 * idx); }( i ));					
					
				}
				
				if (this.currIndex > 2) { this.currIndex = 0 }
				
            }

        },

        dispOverlay: function (theData) {

            var oImg = "";

            // Is there any data?
            if (theData) {

                // Clear any preexisting overlay icon
                window.external.msSiteModeClearIconOverlay();

				// Render the overlay icon based on the data returned...
				if (theData.ntype == "M") {
					oImg = "images/messages-" + theData.num + ".ico";
				} else if (theData.ntype == "R") {
					oImg = "images/requests-" + theData.num + ".ico";
				} else if (theData.ntype == "A") {
					oImg = "images/actions-" + theData.num + ".ico";
				}				

                // Go ahead and create the overlay image and it's label...
                this.setOverlay(oImg, theData.label);

            }

        },

        setOverlay: function (icon, desc) {

            // Sets the overlay icons...
            window.external.msSiteModeSetIconOverlay(icon, desc);
            window.external.msSiteModeActivate();

        },

        init: function (myData) {

            this.dataBin = myData;
			this.getData();
			
        }

    };

    // This clears out any previously set overlay icons...
    window.external.msSiteModeClearIconOverlay();
	
    // Run it once to kick everything off...
    myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
			   ]);

    // This is only here because I want to simulate pulling data on a regular interval...
    setInterval(function () { myPin.getData() }, 10000);

</script>
</body>
</html>

Using Site Pinning and Overlay Icons for Enhanced User Notifications and Engagement

I was recently doing some testing of IE9’s Site Pinning API and found out about a cool bit of functionality that can enhance user notifications. If you’re not familiar with site pinning, it’s a great way to allow users to have easy and quick access to their favorite sites via the Windows taskbar. There’s a really nice overview on Beauty of the Web that explains how it works.

Keeping Users Up-to-Date

One of the features the API provides is the notion of notifications that can allow developers to provide alerts to end users. The functionality allows you to dynamically insert custom overlay icons that can alert users when an important bit of information is available. These overlay icons are rendered over the favicon that is pinned to the taskbar. If you look at the image below, you can see it in action:


Pinned site with no overlay icon


Pinned site with overlay icon

So if you think about the possibilities, any site that offers users an inbox, special deals or sends out time-sensitive alerts could use this notification capability to keep their users up-to-date and more engaged on the site. Sites like the Huffington Post have already discovered that users that pinned HuffPost spent 49% more time on the site.

The best part is that adding this capability is insanely easy.

Setting it Up

For this post, we’re not going to go into the basics of how to pin a site. If you want to learn more, here’s a GREAT resource for getting you up to speed quickly: BuildMyPinnedSite.com. In fact, I used that site to help get me up-to-speed on the basics and it’s well-worth visiting.

To add notifications, you’ll need a couple of things:

  • A cool favicon for your site. If you don’t have one, you can use the handy web-based X-Icon Editor to create one.
  • A set of overlay icons to use. The recommended size is 16×16.

The API is JavaScript-based and we’ll use the following methods:

window.external.msSiteModeClearIconOverlay()
window.external.msSiteModeSetIconOverlay()
window.external.msSiteModeActivate()
window.external.msIsSiteMode()

The window.external.msSiteModeClearIconOverlay method is used to clear out any previously set overlay icons. window.external.msSiteModeSetIconOverlay allows you to specify the name of the notification icon as well as a accessible description. Lastly, we’ll use window.external.msSiteModeActivate to flash the pinned icon to notify the user of the update. Lastly, window.external.msIsSiteMode will let us know if the page was launched as a pinned site, thus allowing us to better determine when to run the code.

For the overlay icons, I’m using five images that display numbers 1 through 5 respectively to designate the number of messages are in a user’s inbox.

The Code

The first thing I need to add is the reference to my favicon. Note that if you don’t add one, then the Internet Explorer’s icon will be used by default.

<link rel="shortcut icon" type="image/ico" href="favicon.ico" />

Next, I want to create some sample data to work with. What I want to do for my demo is to have the overlay icon dynamically change every 5 seconds to simulate a more real-world scenario. The data is a simple array containing JSON data in each element.

myPin.init([{ "num": 1, "label": "Label 1" },
                { "num": 2, "label": "Label 2" },
                { "num": 3, "label": "Label 3" },
                { "num": 4, "label": "Label 4" },
                { "num": 5, "label": "Label 5" }
                ]);

By setting a timer, I’ll be able to pull a new set of data every 5 seconds.

setInterval(function () { myPin.getData(); }, 5000);

The main thing to keep in mind is that I’m “simulating” getting data from some remote host. In reality, all that the myPin.getData() method does is use a running counter to grab a new set of data and render a new overlay icon:

getData: function () {
            // A function that just simulates returning a result set...
            var idx = 0;

            // Determines whether the current page was launched as a pinned site.
            if (window.external.msIsSiteMode()) {

                idx = this.currIndex++;
                this.currIndex = (this.currIndex < 5) ? this.currIndex : 0;

                this.dispOverlay(this.dataBin[idx]);

            }

}

As you can see, it uses the running counter var currIndex to determine which array element to grab and then passes the data to dispOverlay(). This is where we use window.external.msSiteModeClearIconOverlay() to clear out any previously displayed overlay icons and also generate a string for the actual icon name. You can see that the oImg var is created on the fly based on the data we’re using.

dispOverlay: function (theData) {

            var oImg = "";

            // Is there any data?
            if (theData) {

                // Clear any preexisting overlay icon
                window.external.msSiteModeClearIconOverlay();

                // Create the image string...
                oImg = "images/num_" + theData.num + ".ico";

                // Go ahead and create the overlay image and it's label...
                this.setOverlay(oImg, theData.label);

            }

}

That icon name, along with the accessible label text for the icon, is passed to setOverlay() which sets the overlay icon via window.external.msSiteModeSetIconOverlay and flashes the taskbar icon using window.external.msSiteModeActivate.

setOverlay: function (icon, desc) {

            // Sets the overlay icons...
            window.external.msSiteModeSetIconOverlay(icon, desc);
            window.external.msSiteModeActivate();

}

Test it Out

To test this out, it’s a simple matter of running your newly pinned page in Internet Explorer 9, grabbing the tab and dragging it down to your taskbar:


Tab being dragged to the taskbar


Pinned site with no overlay icon

Five seconds after the page has been pinned, the code will fire off the first notification and continue to cycle through the other icons every subsequent five seconds.


Pinned site with overlay icon

An important thing to remember is that the IE F12 Developer tools are available to you to use in debugging your pinned site. So if you run into quirks, simply press the F12 key and the tools will appear.

The Demo and Final Code

You can check out the demo I whipped up by going here in IE9:

http://reybango.com/demos/sitepinning/index.html

When the page renders, drag the tab down to your taskbar and pin it. You should see a new windows appear with your newly pinned site. Five seconds later, you’ll see the first overlay icon appear in the taskbar.

Here’s the full source code. You can also download everything here. The really great part is that it isn’t a lot of code to implement this. In fact, to use the API only required 4 method calls. The bulk of the code was to simulate pulling in data. And the “>impact on user engagement is certainly worth adding in the capability.

<!DOCTYPE html>
<html>
<head>
<title>Pinned Site Test</title>
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />
<meta name="application-name" content="Pinned Site Test" />
<meta name="msapplication-starturl" content="http://reybango.com/demos/sitepinning/index.html" />
<meta name="msapplication-navbutton-color" content="#3480C0" />
<meta name="msapplication-window" content="width=1024;height=768" />
<meta name="msapplication-tooltip" content="Testing the Pinned Site API" />
<style>
body {
    background: none repeat scroll 0 0 #4492CE;
    color: #EDEFF4;
}
 
h1 {
    float: left;
    font: 440%/1.4em 'Segoe Light',Segoe,'Segoe UI','Meiryo Regular','Meiryo',sans-serif;
    margin-left: 10px;
    position: relative;
}
</style>

</head>

<body>

<h1>Pinned Site Test</h1>

<div></div>

<script>

    var myPin = {

        currIndex: 0,
        dataBin: [],

        getData: function () {
            // A function that just simulates returning a result set...
            var idx = 0;

            // Determines whether the current page was launched as a pinned site.
            if (window.external.msIsSiteMode()) {

                idx = this.currIndex++;
                this.currIndex = (this.currIndex < 5) ? this.currIndex : 0;

                this.dispOverlay(this.dataBin[idx]);

            }

        },

        setOverlay: function (icon, desc) {

            // Sets the overlay icons...
            window.external.msSiteModeSetIconOverlay(icon, desc);
            window.external.msSiteModeActivate();

        },


        dispOverlay: function (theData) {

            var oImg = "";

            // Is there any data?
            if (theData) {

                // Clear any preexisting overlay icon
                window.external.msSiteModeClearIconOverlay();

                // Create the image string...
                oImg = "images/num_" + theData.num + ".ico";

                // Go ahead and create the overlay image and it's label...
                this.setOverlay(oImg, theData.label);

            }

        },

        init: function (myData) {

            this.dataBin = myData;

        }

    };

    // This clears out any previously set overlay icons...
    window.external.msSiteModeClearIconOverlay();

    // Run it once to kick everything off...
    myPin.init([{ "num": 1, "label": "Label 1" },
                { "num": 2, "label": "Label 2" },
                { "num": 3, "label": "Label 3" },
                { "num": 4, "label": "Label 4" },
                { "num": 5, "label": "Label 5" }
                ]);

    // This is only here because I want to simulate pulling data on a regular interval...
    setInterval(function () { myPin.getData(); }, 5000);

</script>
</body>
</html>

How to Add Internet Explorer 9 Jump Lists to Your Site

With Internet Explorer 9 Beta is out, I’m poking around with some of the new features available. One of the new features called website pinning allows you to “pin” a website to your Windows 7 taskbar for easy access later. The pinning feature, in itself, isn’t really all that interesting to me because you could create desktop shortcuts to webpages for years. What is cool, though, is the ability specify direct links to important pages from a pinned website. These are called “Jump Lists” and they provide navigation capabilities straight from a pinned website. The obvious use case for this is to allow users to immediately reach the most important pages on a site without having to open the browser, type in the web address and click on several links to get to a page.

The code to make jump lists available is very straightforward. A new meta tag called “msapplication-task” is available in IE9 which allows you to define the navigation for your pinned website. For example, I added the following jump list to my blog:

and here’s the code I added to my site to make that happen:

<meta name="application-name" content="Rey Bango Front-end Developer"/>
<meta name="msapplication-tooltip" content="Launch Rey Bango's Blog"/>
<meta name="msapplication-task" content="name=About;action-uri=/about/;icon-uri=/images/about.ico" />
<meta name="msapplication-task" content="name=The Big List;action-uri=/the-big-list-of-javascript-css-and-html-development-tools-libraries-projects-and-books/;icon-uri=/images/list_links.ico" />
<meta name="msapplication-task" content="name=jQuery Posts;action-uri=/category/jquery/;icon-uri=/images/jquery.ico" />
<meta name="msapplication-task" content="name=Start Developing;action-uri=/category/javascript/;icon-uri=/images/script.ico" />
<link rel="shortcut icon" href="/images/favicon.ico" />

The content attribute allows you to specify the name, action and icon that you’d like to use for your jump list nav entry. Here’s a list of the available meta elements that you can use to format your jump lists:

Name Content
application-name The name of the shortcut. If missing, the document title is used instead.
msapplication-tooltip Optional text that is displayed as a tooltip when the mouse pointer hovers over the pinned site shortcut icon in the Windows Start menu or desktop.
msapplication-starturl The root URL of the application. If missing, the address of the current page is used instead. Only HTTP, HTTPS, or FTP protocols are allowed.
msapplication-navbutton-color The color of the Back and Forward buttons in the pinned site browser window. Any named color, or hex color value as defined by Cascading Style Sheets (CSS), Level 3 (CSS3) is valid. For more information, see Color Table. If this meta element is absent, the color is based on the shortcut icon.
msapplication-window The initial size of the pinned site browser window. Content sub-elements provide size as number N, separated by a semicolon.

  • width=N (minimum 800)
  • height=N (minimum 600)

Note that user action overwrites this value. Windows preserves the user-generated window size when the user changes the size of the window and then closes the instance.

I could further customize the pinned site shortcut with start URL, initial window size, and navigation button color using the following code:

<meta name="msapplication-starturl" content="http://blog.reybango.com/about/"/>
<meta name="msapplication-window" content="width=800;height=600"/>
<meta name="msapplication-navbutton-color" content="red"/>

What About Other Browsers?

Unfortunately, this feature is IE9-specific functionality and not available via other browsers or operating systems; only Windows. I really wish it was more portable because it’s a slick feature and I honestly don’t know if other browser makers could do something like this. Let’s see if they consider adopting this type of UX.

Screencast

To actually see how the pinning works and how jump lists appear, I created a screencast that explains the whole process:

Learn JavaScript!

What to Read to Get Up to Speed in JavaScript.

The best books & blogs for learning JavaScript development. Broken down by experience levels!


My BIG LIST of JavaScript, CSS & HTML Development Tools, Libraries, Projects, and Books.

Constantly updated with the latest and greatest tools. Check it out!

Categories

Rey Bango is Stephen Fry proof thanks to caching by WP Super Cache