Wednesday, August 10, 2011

This Side Up? When Mobile Web Pages Rotate

So I spent a little time this last week working on my "mini mobile CV." It's targeted at mobile devices and was intended originally to demonstrate that I knew how to properly select easy to read fonts and high contrast color schemes for constrained hardware. But as I got into it and saw my device automatically rotate from landscape to portrait mode, I got curious. "What is the best way to detect an orientation change on a mobile device?"

Being familiar with "dark ages" web programming, I originally assumed the solution would involve a fair amount of hackery, reading the heights and widths of transparent divs and so forth. But fortunately, it's not hard at all. Despite recent W3C efforts to define a standard DeviceOrientation Event Specification, it seems many Android devices in the field are copying Apple's Mobile Safari behavior.

You probably want to start by figuring out if the device rendering the page will be generating orientation change events. Do this with the following code snippet:

var supportsOrientation = ( "onorientationchange" in window );

This line checks to see if the 'onorientationchange' property is set in the window object. If it is, then your browser should generate orientationchange events. Next, we want to add an event listener, like so:

if( supportsOrientation ) {
window.addEventListener( "orientationchange", function () { console.log('w00t'); }, false );
}

Of course, printing out a debugging message to the console every time there's an orientation change is vaguely useless, so we may want to consider something beefier. When the orientationEvent fires, you'll want to read window.orientation to find out how many degrees the device has been rotated. But be careful, on some devices, you'll get a shower of these events even though you've only rotated your device once. It's good practice to remember the last orientation value and only "do something" when the orientation actually changes.

var previousOrientation;

function checkOrientation () {
if( previousOrientation !== window.orientation ) {
previousOrientation = window.orientation;
// do interesting things here
}
}

So, putting it all together and adding a bit of callback convenience, you get something like this:

function Rotor ( callback ) {
this.callback = callback;
if( "onorientationchange" in window ) {
this.previous = window.orientation;
callback( this.previous, null );
window.addEventListener( "orientationchange", this, false );
}
}

Rotor.prototype.handleEvent = function ( e ) {
var current = window.orientation;
if( current !== this.previous ) {
this.callback( current, e );
}
this.previous = current;
};

To use it, just instantiate a new Rotor, giving it a callback that knows what to do when the device is rotated.

var roton = new Rotor( function( rotation, event ) { alert( 'current rotation: ' + rotation ); } );

Cheers!