Better client-side storage for popup window

Recently Morpht was asked to create a popup window for a website.

The requirements were as follows:

  • A popup box is to show up a few seconds after the page loads.
  • Users will be able to dismiss the popup box and the box will not show again for the user for 6 hours.
  • There will be a different popup box for each page (the site has a few hundred pages), and they show and hide independently.

Cookies will not work in this case

The first thing that came to mind was to use cookies: when a user clicks a button, the page drops a cookie using the page id and an expire date. When a user loads a page, we check if a cookie with that page id exists, something like this:

document.cookie="id=n12; expires=Thu, 18 Aug 2015 12:00:00 UTC";

However, this method will not work here. Based on this website, it is recommended that each website has no more than 50 cookies(4093 bytes). The website I was working on has hundreds of pages so we are talking about hundreds of instances of popup boxes with their own cookies from the same domain.

Local Storage is a better solution

HTML5 local storage allows websites to store data on a client machine. It has surprisingly good browser support (>IE8). Compared with the storage limit for cookies, local storage has a far larger limit (5MB), that means we can potentially have thousands of pages with their own data stored on a browser.

The reason I chose to use local storage rather then session storage is that session storage lasts only one browser session(it will survive browser refresh). However, creating a local storage object for each page would be inefficient and unnecessary for this case. A better way is to create one single local storage object for all the pages, the value of the storage object will be an array of objects that’s made up of the id of the page and the timestamp of when the popup box was dismissed:

var storage = [{id: "xx", created: "1439861986164"},
               {id: "yy", created: "1439861986154"},
               ...
              ];

To store this data using local storage, we need to use JSON.stringify to convert the storage value into string:

localStorage.setItem("popups", JSON.stringify(storage));

Since local storage is in global scope, all documents of the same origin will be able to read and write this data, here is how we can access it:

var popups = localStorage.getItem("popups");
var popupsArray=JSON.parse(popups);
// popupsArray =[{id: "xx", created: "1439861986164"}, {...}, ...]

Now to determine whether the popup should display or not, we need to check if the id of the page exists in any of the objects in the popupsArray. If it doesn’t, we go ahead and display this popup. If it does exist, then we need to check if it was created more than 6 hours ago. If it was, then we 1. delete that object from the array, 2. display the popup box, and 3. creates a new object with a new timestamp when user dismisses the popup box.

//check if nid exists
if ($.inArray(nid, idArray) === -1) {
  $("body").addClass("popup-open");
  popupClose.click(close);
} else {
  var currentDate = new Date().getTime();
  var time = dateArray[$.inArray(nid, idArray)];
  var compare =  Math.abs(currentDate - time);
  var hr = Math.ceil((compare/(1000*60*60)));
  if (hr < 6) {
    console.log("exist, do nothing");
  } else {
    $("body").addClass("popup-open");
    popupsArray.splice($.inArray(nid, idArray), 1);
    popupClose.click(close);
    //close the popup and update the storage object
  }
}

For better readability I created two temporary new arrays from the popupsArray:

  • idArray: an array of page ids store in the popupsArray
  • dataArray: an array of created property

The jQuery.inArray() function takes two arguments: a value and an array, and it returns the index of that value in the array and returns -1 if not found. Now to close the popup and create/update the storage object:

var close = function(e){
  e.preventDefault();
  var date = new Date().getTime();
  var obj ={
    id: nid,
    created: date
  }
  if (popupsArray.length) {
    popupsArray.push(obj);
  } else {
    popupsArray =[obj];
  }
  $("body").removeClass("popup-open");
  localStorage.setItem("popups", JSON.stringify(popupsArray));
};

In this way, every page will have its own popup box expiry date.