Mobile Web Apps: Ajax

You wouldn’t believe it’s the fifth excerpt from the book “Build Mobile Websites and Apps for Smart Devices” by Earle Castledine, Myles Eftos and Max Wheeler. BuildMobile is publishing the chapter “Mobile Web Apps” from the book, this section is called “Ajax” and its getting serious.

5. Ajax

We’ve now learned how to transition between pages smoothly and without reloading, but so far we’ve only done this with static content. We need to be able to load our pages dynamically, and then transition to them.

The good news is that there’s comparatively excellent support for Ajax on high-end mobile devices—so Ajax work remains largely the same as you’re used to on the desktop. Of course, you have to consider that the average data connection will be an order of magnitude slower (and more expensive), so it’s best to keep your bandwidth use to an absolute minimum.

There are two approaches we could take to loading dynamic data into our application. We could load in the raw data (such as a list of JSON objects representing recently spotted celebrities) and merge it into the application’s HTML—creating list elements and appending them to the page. Or, we could load the entire HTML contents straight from the server and dump it directly into our page. The latter approach sounds more straightforward—so let’s try that first to become familiar with Ajax in the mobile environment. After that, we’ll take a look at handling other data formats.

5.1. Fetching HTML

The first thing we’ll need if we want to retrieve data from a server is … a server! If you try and grab data from a file:// protocol URL (which will be the case if you’re testing pages by double-clicking an index.html file), you’ll hit the dreaded “Access-Control-Allow-Origin” error.

Servers

As this is a book about building mobile web apps in HTML, CSS, and JavaScript, covering the details of setting up a server to deliver your data is unfortunately beyond our scope. We’ll spend the rest of this chapter looking at examples of Ajax functionality we can add to Startrackr, but if you want to try these examples for yourself, you’ll need to set up a server to deliver the appropriate data.

Assuming we have a server—be it running on our machine locally, on a VM, or on the Internet—we now need somewhere to dump the HTML chunks when they’re returned from the server. For this we only require a skeleton of the page we’ve been working with, to act as a container that we fill. Because we’ll be returning the same markup as before, our original CSS will apply to the contents without needing to be modified. Here’s that skeleton:

Example 4.52. ch4/13-ajax.html (excerpt)

<div id="pages">
  <div id="page-spots" class="page-spots"></div>
  <div id="page-spot" class="page-spots"></div>
  <div id="page-sightings" class="page-sightings"></div>
  <div id="page-stars" class="page-stars"></div>
  <div id="page-star" class="page-stars"></div>        
</div>

All the content for each of those sections is back in the spots.html, new.html, and stars.html files—just like a regular website, except that now we’ll be using Ajax to load that content into this empty skeleton.

With this basic HTML in place, the next step is to stop the link from being followed when clicked, as we did with our transition function earlier—with preventDefault(). Then we can execute our Ajax. The jQuery load() function is perfect for our needs: it loads HTML from a URL, and provides a mechanism for choosing which part of the document to return. This is great, because we have no need for the whole page, with the head and meta tags—we only want the contents of the body. Using load() means we don’t need a special version of our HTML page for Ajax, and any updates will only have to be made in one place.

To accomplish this, we use the load() function with a string parameter consisting of the URL we want, followed by a space, followed by a jQuery selector string. The contents of the element matched by that selector will be inserted into the element from which load() was called. The content we want to insert is contained in the .wrapper div of the target page. So, when the Spots link is clicked, we want to call load() on our #spots container and pass in the string “spots.html .wrapper”:

Example 4.53. javascripts/ch4/13-ajax.js (excerpt)

$("#tab-spots a").click(function(e){
  e.preventDefault();
  $("#page-spots").load("spots.html .wrapper");
});

Loading HTML Snippets

If you’re unfamiliar with jQuery, you might be wondering how it loads in a small section of HTML via Ajax. There’s no magic here; it actually loads the entire page and dumps it into a div element that exists outside the DOM. The filtering is done on this element, and the results are inserted in the correct position. Very handy, although of course it means transmitting more data over the network than you actually end up using. For most real-world applications, you’ll probably want to pull data in XML or JSON format and insert it into your HTML on the client side. We’ll be looking at how this can be done shortly. For now, though, we’ll stick with using load() to keep it simple, and focus on demonstrating how the various Ajax methods work, as well as how they’re best used in the context of a mobile app.

This will load the relevant HTML into the right container, but there are a few more tasks that need attention—most notably, making the new content visible! Fortunately, load() allows you to specify a callback function that will be executed once the Ajax call has completed. Inside this callback, we’ll transition the page into view:

Example 4.54. javascripts/ch4/13-ajax.js (excerpt)

$("#tab-spots a").click(function(e){
  e.preventDefault();
  $("#page-spots").load("spots.html .wrapper", function() {
    transition('#page-spots', "fade", false);
  });
});

Adding events to each navigation item is a tedious way to wire up our site. We made an Ajax loader for the Spots page just now, but we’d have to duplicate this code multiple times for it to work for all the links. Instead, we can take advantage of our site’s consistent structure and concoct a system to do it programmatically, based on the contents of the navigation elements. Doing this gives us a level of progressive enhancement: our links are designed to work as normal, but our system intercepts them when clicked and loads the content via Ajax. This method is sometimes known as Hijax.

We’ll then generalize the Ajax code we wrote so that it can be applied to any link we pass it. There are two pieces of data needed for that: the URL to load, and the name of the container to dump it in. This is where having conventions is important. As long as our pages and classes are named in a consistent manner, we can easily create code that targets all our links:

Example 4.5### 5. javascripts/ch4/14-hijax.js (excerpt)

function loadPage(url, pageName) {
  $("#" + pageName).load(url + " .wrapper", function(){
    console.log(this);
    transition("#" + pageName, "fade", false);
  });
};

This function is almost identical to the code from before, except we’ve replaced the page names and URLs with variables. Now we can load a page programmatically, for example:

loadPage(“spots.html”, “spots”);
If fact, we need to load a page by default when the application loads, so we can place that line of code inside the document.ready handler. This will load up the Spots page via Ajax as our home page.

Data Caching

Because the load() method pulls in an entire HTML file, the results can be cached by the browser, which means that changes you make in the page being loaded may not be reflected right away. You can disable the cache globally (for all Ajax requests) with $.ajaxSetup({ cache: false });, or, if it’s just for one particular call, you can append a timestamp to the URL so that each request will be seen by the browser as different. So, instead of loading url + " #wrapper", you can load url + "?" + new Date().getTime() + " #wrapper".

Finally, we want to call our function whenever any navigation items are clicked. Remember, we have to extract two pieces of data to pass to our function: the URL and the page name. The URL is simple—it’s the href value of the link itself. For the page name, there are many approaches we could take: we could give our containers the same names as the files (minus the .html), or we could add some extra data into the link itself. The latter has become easier with the addition of custom data attributes in HTML5, which let us annotate elements with key/value pairs:

Example 4.56. ch4/14-hijax.html (excerpt)

<ul id="tab-bar">
  <li>
    <a data-load="spots" href="spots.html">Spots</a>
  </li>    
  <li>
    <a data-load="sightings" href="new.html">Add a sighting</a>
  </li>
  <li>
    <a data-load="stars" href="stars.html">Stars</a>
  </li>
</ul>

A data attribute starts with data- and is followed by your key name. You can then provide it with whatever value you like. According to the spec, these values should be retrieved using the element’s dataset property; however, this is yet to be widely supported, so your best bet is to use the standard getAttribute() function (as in, myElement.getAttribute("data-load")), or, if you’re using jQuery, the attr() method:

Example 4.57. javascripts/ch4/14-hijax.js (excerpt)

$("#tab-bar a").click(function(e){
  e.preventDefault();
  var url = e.target.href;
  var pageName = $(this).attr("data-load");
  loadPage(url, pageName);
});

Et voilà! Any link inside the #tab-bar element will fire our loadPage() function, and we’ll transition from the current page to the new page. You can easily extend this system to also specify the transition type, if you like.

One current problem is that if the page takes a long time to load, the user has no idea what’s going on (and they’ll probably become click-happy and try to load another page). The obvious solution is a “loading” indicator; jump over to Chapter 6, “Polishing Up Our App” if you’re keen to add one now…

[Ed: Which means you can either buy the book "Build Mobile Websites and Apps for Smart Devices" or wait until we release Section 6 of Chapter 4, "Polishing Up Our App" in the future.]

Build Mobile Book

You can purchase the book “Build Mobile Websites and Apps for Smart Devices” from Sitepoint. Read the whole of Chapter 4. Mobile Web Apps, exclusively here at BuildMobile, for free, in the following sections.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://balupton.com Benjamin Lupton

    Good stuff, although this post doesn’t touch on maintaing history for the state changes at all. So when the user changes the state, the back/forward buttons of their browser won’t update.

    You can add this really easily with History.js which supports the HTML5 History API for mobile devices too, for iOS 4.3 you get it, for other mobile browsers you’ll get a hash fallback.

    You can use this gist – https://gist.github.com/854622 – to enable it all. You can even optimise it further by instead of using jQuery use Zepto instead.

    • http://buildmobile.com Paul Bridgestock

      Hello Benjamin,

      The previous post in the series goes into some detail on history, whereas this is shorter post focusses purely on the Ajax that comes after. The previous post “Loading Pages” has a section called “Going Backwards” that I can jump you straight to, wherein the guys cover managing history all the way down to hardware buttons. Zepto is mentioned in the first post in the series called “Setting Up Shop