文章

A Note on Device Orientation Detection in Javascript

You want to know device height on orientation change in an webview. Here is my journey:

screen.availHeight

I first started with screen.availHeight, it works on Android. When device orientation changed, the number is updated accordingly. But in iOS, this number is fixed to the height when webview started. So I need to do some detection on the device orientation so that the number is right.

function getDeviceHeight(){
  return Math[isLandscape()?'min':'max'](
    screen.availHeight, screen.availWidth
  );
}

How do you implement isLandscape()?

window.orientation

window.orientation will report the degree the device is orientated. In landscape, it’s either 90 or -90. So I try:

function isLandscape(){
  return Math.abs(window.orientation) === 90; 
}

It works in iOS, but there is a problem in Android that when you start the webview in landscape, window.orientation is equals to 0. It’s only after you’ve changed the device orientation that the number would get right. So your initial value could be wrong.

window.matchMedia

There is a new API called matchMedia where you can use to test the orientation. The API exists in iOS5/Android 2.3, which I’m qualified to use.

function isLandscape(){
  var mql = window.matchMedia("(orientation: landscape)");
  return mql.matches;
}

While it seems to work, there is a case in Android that it reports wrong value: when the keyboard is shown in portrait, it reports landscape. You can try to open this example in an Android 4 chrome, simply focus the input would affect the orientation.

window.innerHeight, window.innerWidth

I tried to use the raw window size for detection then:

function isLandscape(){
  return window.innerWidth > window.innerHeight;
}

This method works in both platform alright, but you have to aware of the time of retrieval. In my case, I need the device height value right after orientation changed. I tried to simply hook up with window’s resize event:

$(window).on('resize', function(){ 
  var deviceHeight =  getDeviceHeight();
  //...  
});

When orientation changed, the value retrived is correct. But there is a case in Android, when keyboard is shown, it would also trigger the resize event. I don’t want that. So I changed to use orientationchange event:

$(window).on('orientationchange', function(){ 
   var deviceHeight = getDeviceHeight();
   //...
});

Hm…. this time the retrieved value is incorrect. Someone noted that orientationchange event is fired before resize event, on some devices value like screen.availHeight may not be correctly updated. You can defer it to next event loop:

$(window).on('orientationchange', function(){
  setTimeout(function(){
     var deviceHeight = getDeviceHeight();
     //....
  }, 100);
});

The value is now correct. But this method is actually not reliable as it may vary from device to device. I’ve tested on my targeting devices and came up with the 100 value. So the whole thing:

function getDeviceHeight(){
  return Math[isLandscape()?'min':'max'](
    screen.availHeight, screen.availWidth
  );
}

function isLandscape(){
  return window.innerWidth > window.innerHeight;
}

$(window).on('orientationchange', function(){
  setTimeout(function(){
     var deviceHeight = getDeviceHeight();
     //....
  }, 100);
});

The method is not perfect but the result is fine for now. So let’s temporarily close the case and leave a big chunk of comment. :(

*