Skip to content

Instantly share code, notes, and snippets.

@johan
Last active September 26, 2019 14:19
Show Gist options
  • Save johan/27ba8e53ab756b90cc70 to your computer and use it in GitHub Desktop.
Save johan/27ba8e53ab756b90cc70 to your computer and use it in GitHub Desktop.
LocalStorage Cross-window notifications

Take a website with some kind of notifications system showing the user a count of their unread notifications or similar. (Facebook, Google+, GMail et c.)

Now perform an action in one browser tab, that changes or clears the count. Does this immediately propagate to all other tabs you have open on the site?

If not – this example is for you!

As it happens, this is really easy to do in browsers supporting localStorage (which is all of them, back to 2009; even IE8), by subscribing to the storage event, as demonstrated in this example.

Open this page in two browser tabs or windows, and click the buttons. The event fires on all other windows accessing the same localStorage, so the update will propagate over your whole site by default, as you want.

At the time of posting this (September 23, 2015), none of the social services linked are this kind to their users.

If your site is using alert() or similar (Google Calendar comes to mind) to alert the user of some kind of alarm or scheduled event you have set, similarly: please only fire off that alert in one of all open windows.

You can apply this basic technique for cross-tab synchronization to make sure you don't send off a dozen tabs or more grabbing user focus in a wild dance of confusion, frustration and aggravation.

Thanks for reading, and happy hacking!

<!doctype html>
<html>
<head>
<title>Unseen Notifications</title>
<style>body { margin: 20px; }</style>
</head>
<body>
<h1 id="title">Unseen Notifications</h1>
<button id="add">Add to unseen</button>
<button id="clr">Clear unseen</button>
<script src="notifications.js"></script>
</body>
</html>
var header = document.getElementById('title');
var count = getCount();
var title = document.title.replace(/s$/, '');
updateCount(count);
try {
window.addEventListener('storage', onStorageChange);
} catch(e) {
window.attachEvent('onstorage', onStorageChange);
}
function updateCount(n) {
var s = n === 1 ? '' : 's';
var t = header.innerHTML = document.title = n + ' ' + title + s;
if (frameElement) { // for bl.ocks.org's parent frame
parent.document.title = t;
frameElement.style.height = '125px';
}
}
function getCount() {
return JSON.parse(localStorage.unseenNotificationCount || '0');
}
function setCount(n) {
localStorage.unseenNotificationCount = JSON.stringify(n || 0);
return count = n;
}
function onStorageChange(e) {
if (e.key !== 'unseenNotificationCount') return;
var v = e.newValue;
updateCount(v);
try {
var t = (new Date(e.timeStamp)).toISOString();
console.log('%s: %s = %s', t, e.key, v);
} catch(e) {}
}
document.body.addEventListener('click', function(e) {
switch (e.target.id) {
case 'clr': return updateCount(setCount(0));
case 'add': return updateCount(setCount(1 + getCount()));
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment