Make fast mobile websites and PhoneGap apps you can

I was tasked with building a mobile website and mobile apps for a client to go along with their new website. So, the first thing I did was hit up Google and see what kind of frameworks were available. jQueryMobile 1.1 had just been released, there was jQTouch, Sencha Mobile, Chocolate Chip UI, and so on and so on. Lots of options to choose from, so I needed to decide where to start. I downloaded all of these and took a look at them. Tried out their demos on my phone, and they all shared the same traits. They were heavy, had slow response times, some didn't support one feature or another and they were even slower when paired with PhoneGap. The app I was going to build had a pretty unique design and all of these were a hassle to skin and custom theme. So like any diligent pragmatist, I kept looking. I found libraries like iScroll, HammerJS, iCanHazJS and Zepto. I decided that the large mobile frameworks weren't for me. I needed something custom to keep my app snappy, responsive and fast loading. I came up with a solution using these libraries in their own ways to make my mobile website and PhoneGap app feel just as fast as a native app. I built the entire thing as a single page, and everything is loaded into memory during startup. Let's go over each component.

ZeptoJs

This is a small dom manipulation library that has a jQuery compatible syntax in a much smaller size. It is made almost specifically for mobile webkit, so it was a perfect fit. You can go either way with this one. Some people would prefer

VanillaJS, but in this app I decided to go with Zepto for it's ease of use and I happen to love me some jQuery. I may dive into a VanillaJS implementation at some other time, but I will save that for another post.

iScroll 4 (lite)

iScroll is a very nice library for building touch enabled scrollable areas. It uses CSS 3D transforms to take advantage of mobile browsers hardware acceleration. It saves battery life and the CPU by not using Javascript animations. It is a standalone plugin, so it doesn't need jQuery or Zepto to run. Using it is pretty simple too: HTML:

<div id="scroller_wrap">
  <div class="scroller">

  </div>
</div>
 Javascript:

iScroller = new iScroll('scroller_wrap', {
    vScroll: true,
    vScrollbar: true,
    hScroll: false,
    hScrollbar: false
});
iScroller.destroy();

You instantiate a new iScroll object on the parent container of the element you want to scroll. Save that into a variable for later use. I only used horizontal scroll-able areas, but there are a lot of options available. When you are done with it make sure to destroy it for later use and to free up that memory. Note: I used the lite version of iScroll. This is an alternate download and is a stripped down version with just the basic scrolling functionality and no extra bells and whistles. It's up to you to decide if that is all you need, or if you want the extra features the full version offers.

HammerJS

This is a cool library I found that will take care of handling touch events and has several gestures built in. It was really easy to use and worked great. It even has a nice

jQuery mini plugin you can use it with. I made a quick modification to this to use Zepto by just replacing the jQuery references with a $. Then you bind an event callback to your elements.

$(element).hammer({ prevent_default: true }).bind('tap', callback);

I set the 'prevent_default' to true so the browser won't run it's normal built-in event handlers and conflict with what I want to do. I use this library to handle all touch events. And, the good thing is this library is stand alone, so yet again it does not need jQuery or Zepto to work.

ICanHazJS

This is a really nice lightweight Mustache template engine for Javascript, and another stand-alone library that does not require jQuery or Zepto. It works off of script blocks embedded inside your HTML and spits out raw HTML or a Zepto object that you can do whatever you want with. HTML:

<script id="hello" type="text/html">
    <p>Hello, {{name}}!</p>
</script>
 Javascript:

var details = {
name: 'World'
};
var html = ich.hello(details);
 As you can see, the script block is of type

"text/html". This prevents the browser from trying to run it as javascript. You just give it an id tag, then you can call ich and pass it the values for it to inject into the html block. The html variable is now a Zepto object you can add anywhere you want on the page. You can also pass a second parameter of true and it will return raw html instead.

TouchMySlider

This is a little Zepto plugin I built for my app, but I am throwing it on GitHub for others to get some use out of. This basically lets you create touch slide-able panels not unlike the iPhone and Android home screens. It is dependent on Zepto, HammerJS, and iScroll if you decide to use that extra feature of it. It also has the option to make your slide-able panels contain pages of vertically scrolling content. This comes in handy when you want to page through some screens of long articles. Just not too many panels or too long of content because it will start to slow down. Maybe in a future version I can make it load panels on the fly. Get more info at the TouchMySlider GitHub Repo.

Tying it together

Hopefully you can start to see how these components could be used together. There are a lot of tips and tricks that you may not give a second thought too on the desktop, but when it comes to mobile, every byte of ram and second of battery life is precious. We need to approach things from a different direction to make sure we take care of the user and protect their mobile experience. Now that you have the tools, here are some tips to get you going.

Animations

Today's mobile apps and PhoneGap HTML5 apps have the advantage of more modern browsers that support a lot of CSS3's benefits. You want to use CSS transitions and transforms for everything. Mobile browsers are built to use GPU hardware acceleration for CSS animations instead of the CPU for javascript animations.

Markup

In a mobile browser, small lightweight markup can go a long way. Anytime a page is manipulated in anyway, the browser must redraw the screen. Sometimes many times a second. If your DOM tree is nested several layers deep all over the place, you can see how this could become a problem. Make sure to focus your markup to the minimal amount necessary. Another reason to use CSS transforms is for this exact reason. When you manipulate the position of a DOM element via javascript for animations, the screen needs to redraw itself. Even if the element is absolutely positioned, it needs to ensure nothing else is affected and it forces a redraw. By using 3D transforms such as transform: translate3d(0px, 0px, 0px,) you can bypass this. 3D transforms don't actually move the element. It moves the content around the screen, but as far as the DOM is concerned it is still in the same place. You can also see how this can really speed things up?

Document Scrolling

One problem I kept coming up against was my Document kept wanting to scroll around. I had a fixed sized app, but the browser kept trying to scroll horizontally. I found that I needed to capture all of the touchmove events on the document and static elements to stop this. If this happens to you, here is an easy fix. If this is not happening to you, even better.

document.addEventListener('touchmove', function(e) {
    e.preventDefault();
    return false;
}, false);
 You can also apply this to your static elements like fixed headers and footers.

Retina and other Hi-Res displays

I hate the term 'Retina', but I use it here so people will know what I am talking about. There is no retina display, it's just a high resolution screen for crying out loud. Nevertheless, we need to take these high pixel density screens into account, because they are popping up more and more. Luckily, we live in the land of the modern mobile browser, so it's nothing a CSS media query can't handle. What I did was have a second copy of all of my image assets in a second high-res specific folder. Then I can change the image url in a media query.

@media only screen and (-webkit-min-device-pixel-ratio: 2) {

}

You want to use CSS for all of your image assets as much as possible. This makes it easy to switch them out for high-res screens in one media query. If you do happen to have a need to load in images via your javascript while your users move around your app. I came up with an easy fix that worked out pretty well.

var imgFolder = 'mobile';
if (window.devicePixelRatio >= 2) imgFolder = 'mobile_2x';

So, imgFolder is a global variable that contains the folder name of the images I need to use. Then on page load, if the devicePixelRatio is 2 or greater, just set the variable to your high-res assets. Use this variable throughout to generate your image urls. High-res images need to be twice the size of your normal images. You then set the size of the images to render at the normal size. So, your double sized images will be shrunk in half, but the screen will take full use of all the extra pixels in the image. In CSS it looks like this.

#header h1 {
    background: url(../img/mobile_2x/logo.png) no-repeat center center;
    background-size: 50px 50px;
}

My media query is pulling in my double size logo image. But it's also setting the background size to the normal size. This will force the image to render as a 50px square image even though it's actually 100px in size.

Link tap highlighting

You may have noticed that when you tap on links in a mobile browser, they get surrounded by a hightlight. This is normally fine, but we don't want this on our app because it will quickly break the illusion of a native app. Use this CSS for your links to stop that from happening:

-webkit-tap-highlight-color: rgba(0,0,0,0);
tap-highlight-color: rgba(0,0,0,0);

Text selection

Sometimes, users may have their finger sitting on the screen over some text. Normally this would let them highlight the text to do a copy-paste. We don't want this for the same reason as above. Use this CSS on your body tag to prevent this, or on any other elements it's happening on.

-webkit-user-select: none;
user-select: none;

Wrapup

That should just about do it. Hopefully with this information in hand, you too can get out from under those heavy mobile UI frameworks and keep your sites lightweight and snappy. Let me know what you think in the comments!