How we built our website revamp (Part Two – iPhone and jQTouch)

Development | 4:06 pm

In part one, I explained how we developed our new website revamp. I covered what our initial brief was, a quick overview of our technology stack, framework and CMS as well as covering some specifics of the build itself.  

In this article I will explain how we developed the iPhone specific version of our website and reveal any tips, tricks and hacks we had to employ to get it up and running. Please note, this article assumes you are already quite familiar with jQuery.

The decision to build an iPhone specific version of the site came from the development team and was not part of the original brief. It was a project we undertook as an exercise in testing out what was required to make a website iPhone specific and how long it would take. We also made the decision to write dedicated iPhone based templates within our .NET platform using existing CMS content but offering a tailored iPhone experience rather than simply applying a CSS re-skin on the main site.

Testing environment

To test our iPhone website, we used the iPhone simulator loaded as part of the free iPhone SDK and of course 3G and 3GS handsets themselves. However, for rapid testing during development, we found it more practical to test in Safari with the window resized to approximate the iPhone itself. We also developed using Firefox and Firebug to debug JavaScript and monitor “AJAX” requests to and from the server.

One tip, you can enter the following in your desktop browser’s address bar to get an approximate iPhone size:

javascript:window.scrollTo(0,0);resizeTo(320,480);

Detecting iPhone users

Detecting whether an iPhone user is visiting our website is straight forward enough. It is simply a case of inspecting the user agent string sent in the header of the browser’s request. In .NET this could be as simple as:

if (Request.UserAgent.ToLower().Contains("iphone")){
  Response.Redirect("/iphone/");
}

to redirect iPhone users to a dedicated URL within our site.

We then had to extend this further. When you visit the iPhone version of the site, you have a link to view the “original / desktop” version. This links to the original website’s homepage passing a flag on the query string notifying the server that the user has come from the iPhone site but wishes to view the original site. The code also stores this preference in the user’s session.

Template build and jQTouch

As mentioned earlier, we made the decision to build bespoke iPhone templates but repurpose existing CMS content added for the original site. The templates themselves are straight forward XHTML / CSS. Had we just stuck with this however we would not achieve any transition animations between pages etc. so we decided to try out the open source jQuery based library jQTouch.

jQTouch is a great script which makes it very easy to develop an “iPhone-esque” web experience. It offers such features as: automatically adding iPhone specific META data to the head of your XHTML document (such as adding the viewport scalable setting, specifying an icon to display when the user adds the site to their home screen), native Webkit animations and swipe detection etc.

I won’t cover an in depth usage guide on jQTouch as there are plenty of resources and demos out there. On a simplistic level however, jQTouch works like this:

  • In your XHTML document you need to include jQuery, jQTouch, a mandatory base jQTouch style sheet which includes core CSS3 webkit animations and an optional theme style sheet. jQTouch comes with two themes “Apple” and “JQTouch”. As we wanted the site to be as close a match as possible to the “original / desktop” version, we decide not to use the pre-built themes and instead built out own relatively simple CSS2 based theme.
  • In your XHTML document  you include any number of top level “divs” directly under the <body> tag. Each div represents a single “page” of content that you see in your iPhone browser.
  • jQTouch will automatically hide all but the first top level div by default. You can override this by setting giving a div the CSS class “current”.
  • In your document you set your anchor tags to point to these divs and provide an “animation” CSS class. These tell jQTouch what “page” to display and what transition animation to perform e.g.
    <a class="slide" href="#ContactUs">Contact Us</a>

    will be used by jQTouch to show the “ContactUs” div using the slide animation.

jQTouch and AJAX

The above example is simple and works great, however the main issue with this is that it assumes all of your content is embedded in one XHTML document. This can get a bit heavy if you have a lot of content that users can access. This was the case with our site. Luckily jQTouch comes with simple AJAX support built in to request and cache content on the fly. To include this, it’s just a case of modifying the above anchor tag with a URL e.g.

<a class="slide" href="/iphone/contact-us.htm">Contact Us</a>

jQTouch will automatically call the URL when you first click the link and load the response as a new top level div in your XHTML DOM.

Initially we tried this simple out of the box AJAX support however, we noticed a few things:

  • We didn’t get a nice “Loading…” message for the end user
  • We couldn’t fire a callback function to manipulate the response before adding content to the DOM
  • We noticed the iPhone browser looked like it was simply loading another page directly rather than via AJAX i.e. the address bar would give an indication that a page was loading.

To get around these, there is an example provided in the jQTouch download demonstrating how to manually call AJAX requests by handling the “pageAnimationEnd” event fired. This event is fired by jQTouch when a page has been animated and displayed to the user. The demo works great, however the problem we had with this was that it assumes there is a “blank” related div already in the XHTML DOM waiting for content to be loaded into it.

Pretty much every page of content on the iPhone version of our website is loaded in via AJAX and each page loaded in via AJAX also links to other pages to be loaded in via AJAX. If we followed the above pattern, each page of content loaded from an AJAX request would also have to supply related “blank” divs for any AJAX related content to that page. We did try this approach however, we found too much unnecessary DOM manipulation was happening with each request which slowed down the overall user experience.

To get around this we used the following pattern. What we do is manually handle both the “tap” and “pageAnimationEnd” events of “AJAX” related anchor tags. (See the useful links section for a link to a document describing the “tap” event).

It works like this:

  • We handle the “tap” event of all anchors which have CSS class of “js-ajax”. “js-ajax” is an arbitrary name defined by ourselves, it can be anything you want. When a user “taps” an anchor, the handler then inspects the value of its href attribute:
    • If it starts with a hash “#” the handler assumes content has already been loaded and then exits, allowing jQTouch to carry on.
    • If it doesn’t, it creates a new top level div in the DOM that will hold the content returned to be by the server. The ID it gives this div is based on the href value of the anchor tag e.g. a url of “contact-us.html” would translate to an ID of”contactushtml”. The content of this div is set to display a “Loading” message to the end user and we set a flag against the div to indicate content is to be loaded e.g. $(‘#contactushtml’).data(‘loading’, true);
    • We store the current href value using jQuery.data and modify the href value of the “tapped” anchor to link to this new div replacing the original url e.g. “#contactushtml”, instead of “contact-us.html”.
    • Finally, we then allow jQTouch to continue and perform the transition animation to display the new “Loading” placeholder div.
  • We then handle the “pageAnimationEnd” event which is fired by jQTouch for the “js-ajax” anchor. At this point, the animation will have been played and the anchor’s related div content will be displayed. If the div’s data “loading” flag is false, the handler will end as the content will have already been loaded from the server and added to the DOM. However, if the div’s “loading” flag is true the following happens:
    • We perform an AJAX request to the server using the stored original href of the “tapped” anchor.
    • We then replace the content of the “Loading” div with content returned from the server and set it’s “loading” flag to false.
    • We then ensure the new content is hooked up to the “tap” and “pageAnimationEnd” events so that the pattern continues.

jQTouch, Cufon and anchors with nested element content

In order to get certain features to work, we did have to modify jQTouch here and there. The main modification was triggered because we are using Cufon on a number of our links. We found that links with Cufon based text was not firing. This is due to how the “liveTap” function is written in jQTouch.

The “liveTap” function in version 1.0-beta-2-r109 does the following at line 269 to locate the anchor that was tapped.

// Grab the clicked element
var $el = $(e.target);

if ($el.attr('nodeName')!=='A'){
  $el = $el.parent('a');
}

this works great for simple anchor content e.g.

<a href="#ContactUs">Contact Us</a>
or
<a href="#ContactUs"><span>Contact Us</span></a>

However, if you have nested child elements as part of your anchor content, the above code fails as it only looks to the immediate parent. This causes a problem when you use Cufon as it generates a number of nested child elements. It also causes a problem if your anchor content is of a slightly more complex structure.

To get around this we replaced the above with:

// Grab the clicked element
var $el = $(e.target);

if ($el.attr('nodeName') !== 'A') {
	while ($el.attr('nodeName') !== 'A' &amp;&amp; typeof $el.attr('nodeName') != "undefined"){
		$el = $el.parent();
	}
}

This fix moves up the DOM tree until the first anchor is found or we’ve reached the top of the DOM.

Wrapping up

Hopefully this article has given you an idea how we developed the iPhone version of our site and or experiences with using jQTouch. There are still a number of things we need to look into:

  • We’ve found the tap event to be a bit “laggy” on a standard 3G iPhone
  • We’re not too keen how animations start. When you click a link, the browser scrolls the current page to the top, then fires of the animation to show the next page. This process means when you click “back”, you’ve lost your place in the original page!

Other than these, we’re pleased with the results an the relatively quick turnaround of the site!

Useful links

~Ste

This entry was posted on Friday, February 26th, 2010 at 4:06 pm and is filed under Development. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

2 Responses to “How we built our website revamp (Part Two – iPhone and jQTouch)”

  1. Hank on June 4th, 2010 at 1:07 am

    It drove me nuts till I realized target wasn’t returning the A tag that was clicked, it was returning what was in it.

    jQuery has a built-in function to find the nearest parent tag XX. In your example it would be:

    var $el = $(e.target).closest(‘a’);

  2. Nuno on July 3rd, 2010 at 8:55 pm

    Hi, nice idea, thanks for sharing!

    Can you provide the js part and the html part of the:

    ” . . . It works like this:

    * We handle the “tap” event . . . ”

    Many many thanks in advance
    Nuno Costa