Friday, August 3, 2012

jQuery Mobile - Dynamic Content Area Sizing

Normally web apps are told to simply use document flow to position controls. This way they are able to automatically adjust based on the size of the display. This sounds good on paper but doesn't look so good in real life. Today's web apps especially mobile web apps are judged as much on their form as they are on their function. Knowing the size of the content area makes it easier to place controls using percentages or even pixels, if you are into that kind of thing.

Content Area Sizing

How do you determine the size of the content area? It actually isn't too difficult. Since jQuery Mobile is built on top of jQuery, we have access to jQuery's dimension methods. Determining the width is super simple:

var width = $(window).width();

That's it. Please note that I cache the results into a variable. Searching the DOM is very slow. One of the quickest performance improvements you can make in jQuery or jQuery Mobile code is to cache the results of selectors into variables.

Determining the height is a little trickier. To determine the height of the window is as easy as it was with the width:

var height = $(window).height();

The trick comes with the header and footer. The height of content area changes when one, both, or none of them is present. Luckily that is easy enough to determine as well.

hHeight = $('header').outerHeight() || 0,
fHeight = $('footer').outerHeight() || 0;

Here we take advantage of the JavaScript default operator, also known as the logical or. If the value returned by the selector is null or undefined or something else which is equivalent to false, the variable will be assigned a zero. Also note that we use outerHeight() not height(). We use outerHeight(), because we want the padding and margin too.

The iPhone Problem

There is one last thing that we do which needs explaining. I normally hate to put device specific code in my apps. This is the first exception. The height returned by the iPhone always wants to include the URL bar, even when you have it hidden. I haven't figured out a way around this issue. So now I do a simple, and possibly flawed, detection method for the iPhone. If it is detected we add 60 pixels to the height. I tried for an embarrassingly long time to fix this issue without success. If you have a better workaround, please let me know.

var isIPhone = (/iphone/gi).test(navigator.appVersion),
iPhoneHeight = isIPhone ?  60 : 0,

The Main Events

The final piece of the puzzle is the events. jQuery Mobile documentation will state that you should use the pageinit event to start your own code. In most cases this is true, but since we also want the page to be displayed, we need to wait for the pageshow event. Once we receive it, we know that the header and footer have been rendered. We also want to know when user rotates the screen so we trap the orientationchange event as well. The final event we bind to is the resize event. This is not strictly necessary, since it isn't normally possible to resize a browser window on a mobile device, but I know a lot of people will run the code on their desktop.

The Code

Both the markup and the code for this demo are very simple. An orange div will resize itself to fill up the available content area minus 2 pixels around the edges. In your own app you could potentially place your controls within the div and position them using either a grid or percentages.

Here is the link to a working demo of the app: JQM Resize.
All of the source code is on GitHub: JQM Resize Source Code