Chapter 3. Building for the Mobile Web

The success of any mobile web application relies on two factors: design and performance. For mobile app design, we must have a consistent look and feel across all platforms. For better performance, we must have offline capabilities, animations on the UI, and backend services that retrieve and send data via RESTful or WebSocket endpoints. To put it simply, your app is constrained by two ever-changing speeds: the speed of the device CPU/GPU and the speed of the Internet. The UI is handled by device hardware, such as the GPU, when doing native-like animations and transitions through CSS, and your backend services are limited to the current Internet connection speed of the mobile device.

In this chapter, we’ll discuss how to design, create, and tune your mobile web app to be better looking and more performant. The chapter starts with a brief explanation of how apps should look for mobile devices then jumps into a low-level explanation of hardware-accelerated CSS and how to debug it. From there, you’ll learn what it takes to build an offline mobile application and how to bring all the code together into one application to create a native-like mobile web app that is capable of handling intermittent Internet connections. Lastly, you’ll examine today’s most popular mobile frameworks to get an understanding of when or if you should add a community-supported framework to your project.

The “native versus Mobile Web” debate isn’t about which programming model will win. It’s about what we can build until HTML5-like technologies catch up. We have three choices:

Couple a fast and responsive mobile web app with your existing enterprise infrastructure, and let the games begin. Web standards are quickly closing the gap on missing native features, and device makers are catching up on implementing them. As of Android 3.1, for example, you can capture photos and videos due to the Media Capture API specification.

The W3C is a busy place these days, and developers are moving specifications and better use cases forward. Projects like jQuery are calling on the open source community to participate in these specifications and to submit their ideas for a better Web.

It only makes sense that mobile developers are leaning in favor of writing once, and running their app anywhere. Write once, run anywhere, or WORA, received a lot of fanfare after Sun’s JVM started to emerge in the enterprise. With HTML5, WORA basically means you can use standard JavaScript and CSS to access all of the device features that a native application can (the device GPS, camera, accelerometer, etc.). This approach has given new life to browsers and a language (HTML) that was once only used to serve up documents—not apps.

Spinning refreshes, choppy page transitions, and periodic delays in tap events are just a few of the headaches you face when attempting to create a mobile web app that behaves like a native one. Developers are trying to get as close to native as they possibly can, but are often derailed by hacks, resets, and rigid frameworks.

Normally, GPUs handle detailed 3D modeling or CAD diagrams, but for mobile web apps, we want our primitive drawings (divs, backgrounds, text with drop shadows, images, etc.) to appear smooth and animate smoothly via the GPU. The unfortunate thing is that most frontend developers are dishing this animation process off to a third-party framework without being concerned about the semantics, but should these core CSS3 features be masked? Consider a few reasons why caring about hardware acceleration is important:

To make user interaction smooth and as close to native as possible, you must make the browser work for you. Ideally, you want the mobile device CPU to set up the initial animation, and then have the GPU responsible for only compositing different layers during the animation process. This is what translate3d, scale3d, and translateZ do: they give the animated elements their own layer, thus allowing the device to render everything together smoothly.

CSS features can come at a cost on low-end devices. When using CSS gradient, box-shadow, borders, and background-repeat , you are using the device GPU to paint your images on the fly. CSS can be very powerful for rendering a nice user interface, but you should avoid doing this type of work in software when it can be prebaked in images. This means you should use sprites so the device downloads only a single image and embed data URIs in your CSS files for smaller images.

A few animations that don’t require repaints are:

CSS selector performance can cripple older mobile browsers. Using selectors like:

div[style*='foo']

will severely reduce performance on iOS devices up to version 4.3.x.

Take a look at three of the most common user-interaction approaches when developing a mobile web app: slide, flip, and rotation effects. First, we’ll dissect the slide, flip, and rotation transitions and how they’re accelerated. Notice how each animation requires only three or four lines of CSS and JavaScript. The examples don’t use any additional frameworks, only DOM and vendor prefixed APIs.

Note

You can view this code in action at http://html5e.org/example. The demo is built for a mobile device, so fire up an emulator, use your phone or tablet, or reduce the size of your browser window to 1024px or less.

The most common of the three approaches, sliding page transitions, mimics the native feel of mobile applications. The slide transition is invoked to bring a new content area into the view port.

For the slide effect, first you declare your markup:

<div id="home-page"
class="page">
  <h1>Home Page</h1>
</div>

<div id="products-page" class="page stage-right">
  <h1>Products Page</h1>
</div>

<div id="about-page" class="page stage-left">
  <h1>About Page</h1>
</div>

Notice that the pages are staged left and right. You could place them in any direction, but this is most common.

We now add animation plus hardware acceleration with just a few lines of CSS. The actual animation happens when we swap classes on the page div elements.

.page {
  position: absolute;
  width: 100%;
  height: 100%;
  /*activate the GPU for compositing each page */
  -webkit-transform: translate3d(0, 0, 0);
}

Although translate3d(0,0,0) is known as the silver bullet approach for WebKit, other browser engines like fennec (Mobile Firefox) and Opera Mobile do not support, or are just implementing, translate3d as of this writing. They do support 2D transformations, which cut out the Z-axis, so to support these browsers, you need to change:

transale3d(X,Y,Z);     // or
translateX(X), translateY(Y), translateZ(Z);

to:

translate(X,Y);

The one downside to 2D transforms is that, unlike 3D transforms, they are not GPU accelerated.

When the user clicks a navigation element, we execute the following JavaScript to swap the classes. We’re not using any third-party frameworks yet, this is straight up JavaScript!

function slideTo(id) {
  //1.) the page we are bringing into focus dictates how
  // the current page will exit. So let's see what classes
  // our incoming page is using.
  //We know it will have stage[right|left|etc...]
  var classes = getElement(id).className.split(' ');

  //2.) decide if the incoming page is assigned to right or left
  // (-1 if no match)
  var stageType = classes.indexOf('stage-left');

  //3.) on initial page load focusPage is null, so we need
  // to set the default page which we're currently seeing.
  if (FOCUS_PAGE == null) {
    // use home page
    FOCUS_PAGE = getElement('home-page');
  }

  //4.) decide how this focused page should exit.
  if (stageType > 0) {
    FOCUS_PAGE.className = 'page transition stage-right';
  } else {
    FOCUS_PAGE.className = 'page transition stage-left';
  }

  //5. refresh/set the global variable
  FOCUS_PAGE = getElement(id);

  //6. Bring in the new page.
  FOCUS_PAGE.className = 'page transition stage-center';
}

stage-left or stage-right becomes stage-center and forces the page to slide into the center view port. We are completely depending on CSS3 to do the heavy lifting.

.stage-left {
  left: 100%;
}

.stage-right {
  left: 100%;
}

.stage-center {
  top: 0;
  left: 0;
}

By controlling the animations through swapping the stage classes in JavaScript, we are decoupling the CSS implementations from JavaScript. We could, however, control all the presentation logic within JavaScript by using:

FOCUS_PAGE.style.transform =
"translate(X,Y)";

Each browser vendor may be using a specific vendor prefix for the transform capabilities. One quick way of checking to see what your target browser supports is to use:

var getTransformProperty =
function(node) {
        var properties = [
                        'transform',
                        'WebkitTransform',
                        'msTransform',
                        'MozTransform',
                        'OTransform'
                ];
                var p;
                while (p = properties.shift()) {
                        if (typeof node.style[p] != 'undefined') {
                            document.
                            querySelector("#log").innerHTML += p + "<br/>";
                        }
                }
                return false;
        };

This slide effect has been tested on Mobile Safari, Android, Mobile Firefox (Figure 3-2), and Opera Mobile (Figure 3-3).You can also see the source code that supports all the aforementioned browsers.

On mobile devices, flipping is characterized by actually swiping the page away. Here you can use some simple JavaScript to handle the event on iOS and Android (WebKit-based) devices. Here is an example of flipping in action and the source at github.

When dealing with touch events and transitions, the first thing you’ll want is to get a handle on the current position of the element. Thanks to the CSSMatrix interface, which is implemented by WebKit only at the time of this writing, you can get an instance of WebKitCSSMatrix by passing the current transform’s computed style.

function pageMove(event) {
  // get position after transform
  var curTransform = 
  new WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform);
  var pagePosition = curTransform.m41;
}

Because we are using a CSS3 ease-out transition for the page flip, the usual element.offsetLeft will not work.

Next we want to figure out which direction the user is flipping and set a threshold for an event (page navigation) to take place.

if (pagePosition >= 0) {
 //moving current page to the right
 //so means we're flipping backwards
   if ((pagePosition > pageFlipThreshold) ||
          (swipeTime < swipeThreshold)) {
     //user wants to go backward
     slideDirection = 'right';
   } else {
     slideDirection = null;
   }
} else {
  //current page is sliding to the left
  if ((swipeTime < swipeThreshold) ||
          (pagePosition < pageFlipThreshold)) {
    //user wants to go forward
    slideDirection = 'left';
  } else {
    slideDirection = null;
  }
}

You’ll also notice that we are measuring the swipeTime in milliseconds as well. This allows the navigation event to fire if the user quickly swipes the screen to turn a page.

To position the page and make the animations look native while a finger is touching the screen, we use CSS3 transitions after each event firing.

function positionPage(end) {
  page.style.webkitTransform = 'translate3d('+ currentPos + 'px, 0, 0)';
  if (end) {
    page.style.WebkitTransition = 'all .4s ease-out';
    //page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)'
  } else {
    page.style.WebkitTransition = 'all .2s ease-out';
  }

For this example, ease-out did the trick, but for your own projects, play around with cubic-bezier to give the best native feel to your transitions.

Finally, to make the navigation happen, we must call the previously defined slideTo() methods used in the last example.

track.ontouchend =
function(event) {
  pageMove(event);
  if (slideDirection == 'left') {
    slideTo('products-page');
  } else if (slideDirection == 'right') {
    slideTo('home-page');
  }
}

Next, take a look at the rotate animation being used in this demo. At any time, you can rotate the page you’re currently viewing 180 degrees to reveal the reverse side by tapping on the Contact menu option. Again, this only takes a few lines of CSS and some JavaScript to assign a transition class onclick.

Here's the full source, but here’s the markup (basic concept of front and back):

<div id="front"
class="normal">
...
</div>
<div id="back" class="flipped">
    <div id="contact-page" class="page">
        <h1>Contact Page</h1>
    </div>
</div>

The JavaScript you need is:

function flip(id) {
  // get a handle on the flippable region
  var front = getElement('front');
  var back = getElement('back');

  // again, just a simple way to see what the state is
  var classes = front.className.split(' ');
  var flipped = classes.indexOf('flipped');

  if (flipped >= 0) {
    // already flipped, so return to original
    front.className = 'normal';
    back.className = 'flipped';
    FLIPPED = false;
  } else {
    // do the flip
    front.className = 'flipped';
    back.className = 'normal';
    FLIPPED = true;
  }
}

Finally, here is the relevant CSS:

#back,
#front {
  position: absolute;
  width: 100%;
  height: 100%;
  -webkit-backface-visibility: hidden;
  -webkit-transition-duration: .5s;
  -webkit-transform-style: preserve-3d;
  -moz-backface-visibility: hidden;
  -moz-transform-style: preserve-3d;
  -moz-transition-duration: .5s;
}

.normal {
  -webkit-transform: rotateY(0deg);
  -moz-transform:  rotateY(0deg);
}

.flipped {
  -webkit-user-select: element;
  -webkit-transform: rotateY(180deg);
  -moz-transform:  rotateY(180deg);
}

With the code of the basic transitions covered, take a look at the mechanics of how the transitions run on the device and are composited. Here are a few tips to remember when using accelerated compositing:

To begin debugging, fire up a couple of WebKit-based browsers and your IDE of choice.

Now fire up Chrome to see some good frames per second (FPS) information and borders around the composited layers:

If you view this page in your souped-up version of Chrome, you will see the red FPS counter in the top-left corner, as shown in Figure 3-6.

This is how you know hardware acceleration is turned on. It also gives you an idea of how the animation runs and whether you have any leaks (continuous running animations that should be stopped).

Another way to visualize the hardware acceleration is to open the same page in Safari with the environment variables mentioned above. Every accelerated DOM element will have a red tint to it. This shows you exactly what is being composited by each layer, or accelerated div element. Notice in Figure 3-7, the white navigation is not red because it is not accelerated.

A similar setting for Chrome is also available in the about:flags tab: Click Enable for “Composited render layer borders.”

Another great way to see an example of composited layers is to view the WebKit falling leaves demo (http://www.webkit.org/blog-files/leaves/) while CA_COLOR_OPAQUE=1 is applied. Figure 3-8 shows the results.

Finally, to truly understand the graphics hardware performance of an application, look at how memory is being consumed. Here you can see that the app is pushing 1.38MB of drawing instructions to the CoreAnimation buffers on Mac OS. The CoreAnimation memory buffers are shared between OpenGL ES and the GPU to create the final pixels you see on the screen (Figure 3-9).

When you simply resize or maximize the browser window, you can see the memory expand as well (Figure 3-10).

Using the previous debugging techniques gives you an idea of how memory is being consumed on your mobile device only if you resize the browser to the correct dimensions. When debugging or testing for iPhone environments, for example, resize to 480 by 320 pixels.

This section illustrated how hardware acceleration works and what it takes to debug memory issues or other hardware accelerated glitches. It’s one thing to read about it, but to actually see the GPU memory buffers working visually really brings things into perspective.

Now it’s time to take your page and resource caching to the next level. Much like the approach that jQuery Mobile and similar frameworks use, you can prefetch and cache your pages with concurrent AJAX calls. A few core mobile web challenges highlight the reasons why following this approach makes sense:

You can build on the code from the slide, flip, and rotate demos by adding some secondary pages and linking to them. You can then parse the links and create transitions on the fly.

<div id="home-page"
class="page">
  <h1>Home Page</h1>
  <a href="demo2/home-detail.html" class="fetch">
      Find out more about the home page!
   </a>
</div>

As you can see, this snippet leverages semantic markup with a link to another page. The child page follows the same node/class structure as its parent. You could take this a step further and use the data-* attribute for page nodes, and the like.

Here is the detail page (child) located in a separate HTML file (/demo2/home-detail.html), which will be loaded, cached, and set up for transition on app load.

<div id="home-page-detail"
class="page">
   <h1>Home Page Details</h1>
   <p>Here are the details.</p>
</div>

Now take a look at the JavaScript. For simplicity’s sake, I’m leaving any helpers or optimizations out of the code. The code is looping through a specified array of DOM nodes to dig out links to fetch and cache. For the complete source, see https://github.com/html5e/slidfast/blob/master/slidfast.js#L264.

var fetchAndCache = function()
{
  // iterate through all nodes in this DOM to
  //find all mobile pages we care about
  var pages = document.getElementsByClassName('page');

  for (var i = 0; i < pages.length; i++) {
    // find all links
    var pageLinks = pages[i].getElementsByTagName('a');

    for (var j = 0; j < pageLinks.length; j++) {
      var link = pageLinks[j];

      if (link.hasAttribute('href') &&
      //'#' in the href tells us that this page is
      //already loaded in the DOM - and
      // that it links to a mobile transition/page
         !(/[\#]/g).test(link.href) &&
        //check for an explicit class name setting to fetch this link
        (link.className.indexOf('fetch') >= 0))  {
         //fetch each url concurrently
         var ai = new ajax(link,function(text,url){
              //insert the new mobile page into the DOM
             insertPages(text,url);
         });
         ai.doGet();
      }
    }
  }
};

The use of the AJAX object ensures proper asynchronous post-processing. In this example, you see the basic use of caching on each request and of providing the cached objects when the server returns anything but a successful (200) response.

function processRequest () {
  if (req.readyState == 4) {
    if (req.status == 200) {
      if (supports_local_storage()) {
        localStorage[url] = req.responseText;
      }
      if (callback) callback(req.responseText,url);
    } else {
      // There is an error of some kind, use our
      //cached copy (if available).
      if (!!localStorage[url]) {
        // We have some data cached, return that to the callback.
        callback(localStorage[url],url);
        return;
      }
    }
  }
}

Unfortunately, because localStorage uses UTF-16 for character encoding, each single byte is stored as 2 bytes, bringing our storage limit from 5MB to 2.6MB total. Fetching and caching these pages/markup outside of the application cache scope allows you to take advantage of all the storage space provided by the device.

With the recent advances in the iframe element with HTML5, you now have a simple and effective way to parse the responseText you get back from an AJAX call. There are plenty of 3,000-line JavaScript parsers and regular expressions that remove script tags and so on. But why not let the browser do what it does best? The next example writes the responseText into a temporary hidden iframe. This uses the HTML5 sandbox attribute, which disables scripts and offers many security features. (See complete source.)

var getFrame = function() {
    var frame = document.getElementById("temp-frame");

    if (!frame) {
        // create frame
        frame = document.createElement("iframe");
        frame.setAttribute("id", "temp-frame");
        frame.setAttribute("name", "temp-frame");
        frame.setAttribute("seamless", "");
        frame.setAttribute("sandbox", "allow-same-origin");
        frame.style.display = 'none';
        document.documentElement.appendChild(frame);
    }
    // load a page
    return frame.contentDocument;
};

var insertPages = function(text, originalLink) {
  var frame = getFrame();
  //write the ajax response text to the frame and let
  //the browser do the work
  frame.write(text);

  //now we have a DOM to work with
  var incomingPages = frame.getElementsByClassName('page');

  var pageCount = incomingPages.length;
  for (var i = 0; i < pageCount; i++) {
    //the new page will always be at index 0 because
    //the last one just got popped off the stack with
    //appendChild (below)
    var newPage = incomingPages[0];

    //stage the new pages to the left by default
    newPage.className = 'page stage-left';

    //find out where to insert
    var location = newPage.parentNode.id ==
                                  'back' ? 'back' : 'front';

    try {
      // mobile safari will not allow nodes to be transferred from one
      // DOM to another so we must use adoptNode()
      document.getElementById(location).
                   appendChild(document.adoptNode(newPage));
    } catch(e) {
      // todo graceful degradation?
    }
  }
};

The target browser (Mobile Safari) correctly refuses to implicitly move a node from one document to another. An error is raised if the new child node was created in a different document. So this example uses adoptNode, and all is well.

So why iframe? Why not just use innerHTML? Even though innerHTML is now part of the HTML5 spec, it is a dangerous practice to insert the response from a server (evil or good) into an unchecked area. innerHTML has also been noted to fail intermittently on iOS (just do a Google search on “ios innerhtml” to see the latest results) so it’s best to have a good workaround when the time comes.

Figure 3-11 shows the latest performance test from http://jsperf.com/ajax-response-handling-innerhtml-vs-sandboxed-iframe. It shows that this sandboxed iframe approach is just as fast, if not faster than innerHTML on many of today’s top mobile browsers. Keep in mind the measurement is operations per second, so higher scores are better.

Now that we have the ability to buffer (or predictive cache) the example web app, we must provide the proper connection detection features to make the app smarter. This is where mobile app development gets extremely sensitive to online/offline modes and connection speed. Enter the Network Information API. With it, you can set up an extremely smart mobile web app.

When would this be useful? Suppose someone on a high-speed train is using your app to interact with the Web. As the train rushes along, the network may very well go away at various moments, and various locales may support different transmission speeds (HSPA or 3G might be available in some urban areas, while remote areas might support much slower 2G technologies only). Not only does the following code address connection scenarios like this, it also:

Again, all of these features require very little code. The first step is detect the events and loading scenarios (see https://github.com/html5e/slidfast/blob/master/slidfast.js#L536):

window.addEventListener('load', function(e) {
 if (navigator.onLine) {
  // new page load
  processOnline();
 } else {
   // the app is probably already cached and (maybe) bookmarked...
   processOffline();
 }
}, false);

window.addEventListener("offline", function(e) {
  // we just lost our connection and entered offline mode,
  // disable external link
  processOffline(e.type);
}, false);

window.addEventListener("online", function(e) {
  // just came back online, enable links
  processOnline(e.type);
}, false);

In the EventListener statements above, we must tell the code if it is being called from an event or an actual page request or refresh. The main reason is because the body onload event won’t be fired when switching between the online and offline modes.

The simple check for an online or onload event below resets disabled links when switching from offline to online. For a more sophisticated app, you could also insert logic that would resume fetching content or handle the UX for intermittent connections.

function
processOnline(eventType) {

  setupApp();
  checkAppCache();

  // reset our once disabled offline links
  if (eventType) {
    for (var i = 0; i < disabledLinks.length; i++) {
      disabledLinks[i].onclick = null;
    }
  }
}

For the processOffline() function, you could manipulate your app for offline mode and try to recover any transactions that were going on behind the scenes. The code below crawls the DOM for all of the external links and disables them, trapping users in our offline app—forever!

function processOffline() {
  setupApp();

  // disable external links until we come back
  // setting the bounds of app
  disabledLinks = getUnconvertedLinks(document);

  // helper for onlcick below
  var onclickHelper = function(e) {
    return function(f) {
      alert('This app is currently offline and cannot access the hotness');
      return false;
    }
  };

  for (var i = 0; i < disabledLinks.length; i++) {
    if (disabledLinks[i].onclick == null) {
      //alert user we're not online
      disabledLinks[i].onclick = onclickHelper(disabledLinks[i].href);

    }
  }
}

Okay, suppress your evil genius laugh, and let’s get on to the good stuff. Now that the app knows what connected state it’s in, we can also check the type of connection when it’s online and adjust accordingly with the code below. In the comments, I listed typical North American providers’ download and latencies for each connection.

function setupApp(){
  // create a custom object if navigator.connection isn't available
  var connection = navigator.connection || {'type':'0'};
  if (connection.type == 2 || connection.type == 1) {
      //wifi/ethernet
      //Coffee Wifi latency: ~75ms-200ms
      //Home Wifi latency: ~25-35ms
      //Coffee Wifi DL speed: ~550kbps-650kbps
      //Home Wifi DL speed: ~1000kbps-2000kbps
      fetchAndCache(true);
  } else if (connection.type == 3) {
  //edge
      //ATT Edge latency: ~400-600ms
      //ATT Edge DL speed: ~2-10kbps
      fetchAndCache(false);
  } else if (connection.type == 2) {
      //3g
      //ATT 3G latency: ~400ms
      //Verizon 3G latency: ~150-250ms
      //ATT 3G DL speed: ~60-100kbps
      //Verizon 3G DL speed: ~20-70kbps
      fetchAndCache(false);
  } else {
  //unknown
      fetchAndCache(true);
  }
}

There are numerous adjustments you could make to the fetchAndCache process, but the example code simply tells it to fetch the resources asynchronous (true) or synchronous (false) for a given connection. To see how this works in practice, consider the edge (synchronous) request timeline shown in Figure 3-12 and the WiFi (asynchronous) request timeline shown in Figure 3-13.

The example code allows for at least some method of user experience adjustment based on slow or fast connections, but it is by no means an end-all-be-all solution. Another improvement would be to throw up a loading modal when a link is clicked (on slow connections) while the app still may be fetching that link’s page in the background. Your overall goal is to cut down on latencies while leveraging the full capabilities of the user’s connection with the latest and greatest HTML5 has to offer. You can view the network detection demo at http://html5e.org/example.

It seems like there’s a new JavaScript-based mobile framework popping up every day. You can literally spend days (or months) comparing frameworks and whipping up multiple proofs-of-concept (POCs), only to find out that you may not want or need a framework at all.

In the majority of situations, whether converting an existing app or starting from scratch, you’re better off writing your own CSS and DOM interactions. The harder you lean on a framework, the harder your app will fall when problems arise. Knowing the basics and how to fix those problems under the hood are essential. The DOM is the underlying infrastructure and API for all web apps. No matter how much you like or dislike the API, if you desire a mobile web app that screams at blazing fast speeds and gets “close to the metal,” you must understand how to work with it.

One commonly used programming model for the Mobile Web is called single page. This means you put your entire markup into a single HTML page, often enclosed by a <div> or some other sensible block element, as in this sample single-page web app structure:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <body>

    <div id="home-page">
      ...page content
    </div>

    <div id="contact-page">
      ...page content
    </div>

  </body>
</html>

Why put everything in one page? Primarily, it buys you native-like transitions and fewer initial HTTP requests. You must use AJAX and CSS3 transitions to emulate the feel of a native application and load data dynamically. This single-page approach also promotes including all your resources, such as JavaScript and CSS, within the file. Again, this reduces additional HTTP requests to get the best performance possible from your mobile application.

With an understanding of the basics, consider a few mobile-focused JavaScript frameworks that try to take care of the heavy lifting on the UI. Most of today’s JavaScript frameworks have a specific browser or platform they’re targeting. Some are WebKit-only and others try to span all device browsers. There may be features you need, and ones you don’t. So it’s up to you to decide when to bring any framework into your current or existing project.

Warning

Some mobile frameworks extend or build on older, bloated, desktop-browser frameworks. Be careful that whichever framework you choose does not check for older IE6 bugs or platforms that you aren’t targeting. This bloat may seem minimal to some, but as you will see in the next chapter, every byte you can shave off the initial load time will greatly enhance the user experience.

When evaluating mobile JavaScript frameworks, look for:

Finally, investigate the programming model uses and ask yourself: does my project require a dynamically generated UI through JavaScript, or do I want to declare my markup beforehand in the single-page approach?

The framework smackdown in the following sections provides an overview of the three main approaches to mobile web apps development: single page, no page structure, and 100% JavaScript-driven.

As previously mentioned, the single-page approach forces you to put as much markup and resources as possible into a single HTML file. In the end, this limits HTTP requests for a better performing app. The leaders here are jQuery Mobile and jQTouch.

jQuery Mobile (http://jquerymobile.com; demo at http://jquerymobile.com/test) is strictly tied to the release schedule of the core jQuery library. Known for its AJAX-based navigation system and themeable ThemeRoller designs, the framework is produced by the core jQuery project. It also has an attractive set of widgets, but unfortunately, they’re all decorated with CSS background gradients, text shadows, rounded corners, and drop shadows. As you’ll see in the coming chapters, heavy use of CSS decorations in mobile web apps can slow the browser to a crawl.

jQuery Mobile is the most popular mobile web framework out there today. Taking into account its over 10,000 followers on Twitter and more than 6,000 watchers on github (Figure 3-14), you can easily see the power piggy-backing on an existing project’s success (in this case, core jQuery) to catapult a project into the mainstream. The real power and strength of this project comes from its community. Table 3-1 gives a high-level snapshot of the jQuery Mobile project.

To set up the page, use the code:

<!DOCTYPE html>
<html>
    <head>
    <title>My Page</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/jquery.mobile-1.0.min.css" />
    <script type="text/javascript" src="/jquery-1.6.4.min.js"></script>
    <script type="text/javascript" src=" /jquery.mobile-1.0.min.js"></script>
</head>
<body>

<div data-role="page">

    <div data-role="header">
        <h1>My Title</h1>
    </div><!-- /header -->

    <div data-role="content">
        <p>Hello world</p>
    </div><!-- /content -->

</div><!-- /page -->

</body>
</html>

To set up a component such as the one shown in Figure 3-15, use:

<ul data-role="listview"
data-inset="true" data-filter="true">
    <li><a href="#">Acura</a></li>
    <li><a href="#">Audi</a></li>
    <li><a href="#">BMW</a></li>
    <li><a href="#">Cadillac</a></li>
    <li><a href="#">Ferrari</a></li>
</ul>

jQTouch is a Zepto/jQuery plug-in and a good, simple framework to get started with quickly. It offers a basic set of widgets and animations but lacks support for multiple platforms. The framework also suffers from slow, flickering animations and delayed tap events. Supporting only iOS and Android, jQTouch is the second most popular framework on the interwebs with more than 9,000 Twitter followers and a nice following on github (Figure 3-16). However, the commit history in github looks a little sparse, with six-month gaps at times. Table 3-2 outlines its features. (Check out the jQTouch demo.)

To set up the page, use the code:

<html>
    <head>
        <Title>My App</title>
    </head>
    <body>
        <div id="home">
            <div class="toolbar">
                <H1>Hello World</h1>
            </div>
            <ul class="edgetoedge">
                <li class="arrow"><a href="#item1">Item 1</a></li>
            </ul>
        </div>
    </body>
</html>

To set up the component shown in Figure 3-17, use:

<ul class="edgetoedge">
        <li class="arrow"><a id="0" href="#date">Today</a></li>
        <li class="arrow"><a id="1"
href="#date">Yesterday</a></li>
        <li class="arrow"><a id="2" href="#date">2 Days
Ago</a></li>
        <li class="arrow"><a id="3" href="#date">3 Days
Ago</a></li>
        <li class="arrow"><a id="4" href="#date">4 Days
Ago</a></li>
        <li class="arrow"><a id="5" href="#date">5 Days
Ago</a></li>
</ul>

If you prefer to create your user interface programmatically, without touching much markup, then the 100% JavaScript-driven approach may be your best option. Out of this approach, Sencha Touch, Wink Toolkit, and The-M-Project are three of the top projects.

An HTML/CSS3/JavaScript framework, Sencha Touch offers a variety of native-style widgets, flexible theming via SASS/Compass, data-feature-like models, stores, and proxies. Enhanced touch events and a strong data model give this framework a bit of an enterprise edge without a ton of coverage across devices (see Table 3-4). Although not in a github repository, Sencha Touch currently has around 800 followers on Twitter. (See the Sencha Touch demo.)

If you choose Sencha Touch, be aware it is a specific way of life for mobile developers. Much like GWT or JSF, you are tied to a specific development model for creating user interfaces. In jQTouch or jQuery Mobile, you write specially structured HTML. When it loads, the library reconfigures the page and turns your regular links into AJAX-based animated ones. With Sencha, you basically don’t write HTML at all, but instead, you build your UI and app with JavaScript, so be prepared for a learning curve.

For your page setup, use the code:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width; initial-scale=1.0;
maximum-scale=1.0; minimum-scale=1.0; user-scalable=0;" />
    <link rel="stylesheet" href="/sencha-touch.css" type="text/css">
    <title>List</title>
    <script type="text/javascript" src="/sencha-touch.js"></script>
</head>
<body></body>
</html>

JavaScript handles setup of your component, as well as the entire app (Figure 3-19):

Ext.setup({
    tabletStartupScreen: 'tablet_startup.png',
    phoneStartupScreen: 'phone_startup.png',
    icon: 'icon.png',
    glossOnIcon: false,
    onReady : function() {
        Ext.regModel('Contact', {
            fields: ['firstName', 'lastName']
        });

        var groupingBase = {
            itemTpl: '<div class="contact2"><strong>{firstName}</strong>
{lastName}</div>',
            selModel: {
                mode: 'SINGLE',
                allowDeselect: true
            },
            grouped: true,
            indexBar: false,

            onItemDisclosure: {
                scope: 'test',
                handler: function(record, btn, index) {
                    alert('Disclose more info for ' +
                                  record.get('firstName'));
                }
            },

            store: new Ext.data.Store({
                model: 'Contact',
                sorters: 'firstName',

                getGroupString : function(record) {
                    return record.get('firstName')[0];
                },

                data: [
                    {firstName: 'Hello', lastName: 'World'},
                ]
            })
        };


        if (!Ext.is.Phone) {
            new Ext.List(Ext.apply(groupingBase, {
                floating: true,
                width: 350,
                height: 370,
                centered: true,
                modal: true,
                hideOnMaskTap: false
            })).show();
        }
        else {
            new Ext.List(Ext.apply(groupingBase, {
                fullscreen: true
            }));
        }
    }
}) 
 

The Wink Toolkit project started in early 2009 at Orange Labs (France Telecom R&D). Since June 2010, Wink has been a project of the Dojo foundation. Wink’s core offers all the basic functionalities a mobile developer would need from touch event handling to DOM manipulation objects and CSS transforms utilities (Table 3-5). Additionally, it offers a wide range of UI components. Currently, its online following is low, as evidenced in Figure 3-20. (See their demo).

The coolest thing about Wink is their vast set of 2D and 3D components, and the ability to manipulate components with gestures. For example, with Wink’s Cover Flow component, the user can use two fingers to alter the perspective (Figure 3-21).

The HTML for page setup is:

<html>
    <head>
        <link rel="stylesheet" href="wink.css" type="text/css" >
        <link rel="stylesheet" href="wink.default.css" type="text/css" >
        ...
        <script type="text/javascript" src="wink.min.js"></script>
        ...
    </head>
    <body onload="init()">
    <div class="w_box w_header w_bg_dark">
        <span id="title">accordion</span>
        <input type="button" value="home"
        class="w_button w_radius w_bg_light w_right"
      onclick="window.location='..?theme='+theme"/>
    </div>

    <div class="w_bloc">
        click on the accordion section below to display the content.
    </div>

    <div id="output" style="width: 95%; margin: auto">
    </div>
</body>
</html>

To set up the component shown in Figure 3-22, use:

var accordion, section1,
section2, section3;

init = function()
{
    accordion = new wink.ui.layout.Accordion();

    section1 = accordion.addSection('Section1', 'Hello World');
    section2 = accordion.addSection('section2', '...');
    section3 = accordion.addSection('section3', '...');

    $('output').appendChild(accordion.getDomNode());
}

deleteSection = function()
{
    accordion.deleteSection(section2);
     
     

The-M-Project (their demo ) is built on top of jQuery and jQuery Mobile. It uses concepts and parts from SproutCore and bases its persistence handling on persistence.js. Figure 3-23 gives a snapshot of its community following.

Because The-M-Project UI looks exactly like jQuery Mobile, it’s hard to tell at first glance what the big difference is. The project, however, is much more than a shiny UI framework. It has four core development concepts: MVC, Content Binding, Dynamic Value Computing, and Event Handling. So unlike the UI-focused Wink Toolkit, The-M-Project puts most of its focus on the programming model, as you can see in Table 3-6.

A bit of JavaScript handles page setup:

PageSwitchDemo.Page1 =
M.PageView.design({
    childViews: 'header content',
    header: M.ToolbarView.design({
        value: 'Page 1'
    }),
    content: M.ScrollView.design({
        childViews: 'button',
        button: M.ButtonView.design({
            value: 'Goto Page 2',
            events: {
                tap: {
                    target: PageSwitchDemo.ApplicationController,
                    action: 'gotoPage2'
                }
            }
        })
    })

});

To create the component shown in Figure 3-24, use:

M.SelectionListView.design({

    childViews: 'item1 item2 item3 item4',
    /* renders a selection view like radio buttons */
    selectionMode: M.SINGLE_SELECTION,

    item1: M.SelectionListItemView.design({
        value: 'item1',
        label: 'Item 1',
        isSelected: YES
    }),
    item2: M.SelectionListItemView.design({
        value: 'item2',
        label: 'Item 2'
    }),
    item3: M.SelectionListItemView.design({
        value: 'item3',
        label: 'Item 3'
    }),
    item4: M.SelectionListItemView.design
        value: 'item4',
        label: 'Item 4'
    })
});

In the world of desktop-based web development, we have many tools at our disposal for debugging. Firebug and Chrome’s developer tools are a few that help us get the job done faster. For mobile, the situation is much different, and we must remotely debug through third-party tools. Luckily, projects like weinre, Adobe Shadow, and Opera’s Remote Debugging tools try to give developers the same debugging experience as desktop environments.

Shown in Figure 3-26, Adobe Shadow is an inspection and preview tool that streamlines the preview process for Android and iOS mobile devices. After installing Shadow on your computer, you’ll be able to wirelessly pair your devices, have them browse in sync with your computer, and perform remote inspection and debugging so you can see HTML/CSS/JavaScript changes instantly on your device. Some of Shadow’s features include the ability to:

  • Wirelessly pair your iOS devices to your computer

  • Synchronously browse with your computer

  • Target a device for debugging and select an element in the DOM

  • Make changes to your HTML markup

  • Tweak your CSS rules

  • See changes instantly on your device

Using the remote debugging functionality of Opera Dragonfly, you can analyze and debug pages running in the Opera Mobile Emulator (see Figure 3-27). With Dragonfly, you can debug in separate instances of the Opera browser, as well as other Opera Presto-powered user agents. It doesn’t matter if these are located on the same machine or on another device such as a mobile phone or television. When put into Remote Debugging mode, Opera Dragonfly will listen for a connection to the IP address and port specified. The separate instance of the Opera browser can connect over a network and pass debugging information across the connection. Opera Dragonfly can then interact with the web pages and applications on the remote instance, just as if it were running locally.