A mobile web app using Ajax and localStorage

Sometimes one gets so involved in a project that one forgets to write about it. That's been me these past couple of weeks, but hopefully this post will rectify that with insightful analysis. The only thing worse than documentation is no documentation.

The project is called PIX and its objective is to provide an optimal mobile interface for viewing sets of Flickr photos. The m.flickr.com site seems to me less than optimal since there is no way I could find to view photos full-screen.

Click here for the demo of PIX.

Here are the features of PIX.

  • Display a grid of thumbnails by clicking/touching a link.
  • Display a full-screen width photo by clicking/touching a thumbnail.
  • Zoom in on a full-screen width photo by clicking/touching it.
  • Allow users to add and delete links.
  • Use the HTML5 feature localStorage to save and restore the links the user chooses.

That's a tall order for sure and may require a series of posts. I have run into a couple instances of features working in Firefox and even Safari on my PC and not working on the iPhone and have found solutions for those (with days of research and testing).

Note that this app fails if javascript is not on, but that's okay since the primary target audience here is smartphones, i.e. iPhones and Android - both have great Webkit-based browsers and support for HTML5. The app will work on modern browsers whether mobile or desktop.

So it's a fairly simple web site: A bookmarking site - a list of links which fire a javascript, in this case to fetch a feed via ajax by using the jQuery.getJSON() method. The list of links can be customized by the user by using the "+" and "=" buttons.

This is the basic PIX interface. It has a list of links styled into mobile-style buttons.

The HTML code is fairly simple. It is an HTML5 document (). If you view the source code, you will see that the page is a series of divs, most of which are hidden by the CSS and then shown by jQuery. Going from last to first:

  • #home - this div holds the list of links which is created by javascript when the page loads.
  • #content - Javascript fetches the feed from Flickr and shows the thumbnail images here (at the same time, hiding the #home div).
  • #deleteform - is partially hard coded into the HTML. Javascript lists the links from local storage with checkboxes. Note that the "#deletebutton" span is hard coded into the HTML. This avoids the problem of jQuery not being bound to dynamically loaded objects.
  • #theform - This form which allows a user to add links is completely hard coded in the HTML.
  • #help - This field which explains the functionalities of the various interface elements is displayed if a user clicks on the "?" icon.
  • #header - which shows the site name and the "buttons".

Let's look at some of the script.

When the page loads, javascript gets the localStorage and iterates over it and builds the list items.

Before that it checks for local storage and pops up an alert if local storage is not supported. If local storage is supported but empty, the script adds a couple sample links to local storage. Originally I had the sample links hard-coded into the HTML, but decided to put them in local storage so the user could delete them if he wanted to.


$(document).ready(function() {

  $("#content").hide();
  $("#home").show();
	
  if (typeof(localStorage) =='undefined') {
    alert('Local storage not supported by this browser.');
  }
	
  var mycount = localStorage.length;
	
  //this populates localStorage with a few links if there are none
  if (mycount<1) {
    localStorage.setItem("Sea and Sky",
       "loadFlickr('72157620906549765','37054878@N03')");
  localStorage.setItem("Grand Rapids",
       "loadFlickr('72157621350530834','37054878@N03')");
  }
	
  for ( var i=0, len=mycount; i<len; i++ ){
    var myitem = localStorage.key(i);
    var mylink = localStorage.getItem(myitem); 
    var myli = "<li><a href=\"javascript:" + mylink + "\">" + myitem + "</a></li>";
    $('#nav').append(myli);
  }

Jquery loads the links into the ul with the id of "nav". Each one like this: <li> <a href="javascript:loadFlickr('72157622667924206','37054878@N03')"> Casper and the Cookies </a> </li>.

Next the script loads a series of "click" functions to handle clicks (touches) on various objects. First comes the function to handle clicks on the buttons (links) in the #nav list. JQuery gets href of what was clicked on, makes sure it contains "loadFlickr", splits the href, and then by running eval on the second half of the href essentially calls the loadFlickr function passing it the paramenters it needs to do its job. The loadFlickr function will be discussed later.


$("#nav li, #nav li a").click(function(){
  var mylink = (this.firstChild);
    if ((mylink.href).indexOf("loadFlickr") != -1) {
      var myfunc = (mylink.href).split(":");
      eval(myfunc[1]);
      return false;
    } else {
      $("#content").hide();
      $("#home").show();
      return false;
      endif };
    })

Quite a few of the "click function" are quite simple and consist of simply hiding and showing certain divs. A future improvement to work on would be to add some jQuery animatiion to these transitions - right now just .show() and .hide() are used.

Local Storage

localStorage is a new feature of HTML5 which allows data to be stored in the client browser (like a cookie but better). The data is stored in key-value pairs. In this case, the user supplies a title and the feed URL, and I use the title as the key and extract parts of the URL (the user id and the set id) for the value.

There are 3 occasions when the app accesses the localStorage: when the page first loads, when the user adds a link, and when a user deletes a link.

In the first script above you can see that when the document is ready, I check the length of localStorage and if it is empty a couple of links are added to localStorage using the setItem method: "localStorage.setItem("Sea and Sky",
"loadFlickr('72157620906549765','37054878@N03')");

The syntax is localStorage.setItem(key, value).

A few lines after that the app loops through localStorage and builds the list of links using the getItem method.


    var myitem = localStorage.key(i);
    var mylink = localStorage.getItem(myitem); 

In this case, I get the key using "localStorage.key(i);" (where i is a number which is imcrementing from zero to the number of items in localStorage) and then use the key to get the value with getItem method for which the syntax is "localStorage.getItem(key);".

When a user adds a link, the following function is executed:


function addlink() {
   
  var thetitle = $("input#title").val(); 
  var myurl = $("input#url").val();
	
  var theurl =(decodeURIComponent(myurl));
	
  $("#home").hide();
  $("#help").hide();
  $("#content").empty();
	
  var hashes = theurl.split('?');
  var theparams = hashes[1];
  var theitems = theparams.split('&');
  var thensid = theitems[1].split('=');
  var nsid = thensid[1];
  var theset = theitems[0].split('=');
  var myset = theset[1];
  mystring = "loadFlickr('" + myset + "','" + nsid + "')";
  localStorage.setItem(thetitle, mystring);
	
  $("input#title").empty(); 
  $("input#url").empty();
	
  window.location.reload()
	
}

When the user submits the form, the title and the feed URL are put into variables. The URL variable is split a couple times to extract the user id and the set id, so I can build the localStorage value into something like "loadFlickr('72157620906549765','37054878@N03')"); then I use the setItem method to add it to localStorage.

When a user wants to delete a link, he is shown a list of all the keys (link titles) from localStorage with a checkbox next to each. This list is built by looping through localStorage with the getItem method just like when the list of links is built except a checkbox is added to the string. When a user submits the form, if a title has a checked checkbox, it is removed from localStorage using the removeItem method.


$("#deletebtn").click(function(){
  var checkons = $('#innerform').html();
  $("#innerform input").each(function (i) {
  if($(this).attr('checked')){
    var newitem = $(this).attr('value');
    var really = confirm("Really remove the link to " + newitem + "?");
    if (really==true) {
      localStorage.removeItem(newitem);
    }
  }
  });
  window.location.reload();
});

Notice that when adding and removing items the last thing we do is reload the page using "window.location.reload();". When the page reloads, it runs the function that builds the list of links which will reflect the new addition or deletions.

This is getting too long, so I may discuss the loadFlickr function in another article. As always, you're welcome to view source. Any questions, ask them in the comments. Ciao!