Philippe Masset

Engineer at Buffer.

Cross-device, cross-browser portrait-landscape detection

July 2012

One quick disclaimer before getting started: this article is about finding out whether a mobile device is in portrait or landscape mode; we don't care about its orientation. For all we know, if you hold your iPad upside down, it's in portrait mode.

Now, at the time I write this post, one could argue there already is an easy and cross-everything way to detect the orientation of a mobile device, and that would be the window.orientation property.

It's updated each time the screen's orientation changes, and takes one of these four values: -90 for "rotated to the left", 0 for "default orientation", 90 for "rotated to the right" and finally 180 for "upside down".

How neat! Let's just wait for the window.onorientationchange event and check what's inside window.orientation!

// DO NOT use this
$(window).bind("orientationchange", function(){
    screenOrientation = window.orientation;
});

Easy peasy, lemon... wait! We're not silly; some browsers just won't support it, so let's take care of that case by using the window.onresize event when window.onorientationchange isn't available. And let's do the same for window.orientation by emulating it when needed:

// still DO NOT use this
// listen to either window.onorientationchange or window.resize depending on their availability
$(window).bind(("onorientationchange" in window)? "orientationchange" : "resize", function(){
    // window.orientation is available
    if("orientation" in window){
        screenOrientation = window.orientation;
    // window.orientation is NOT available: emulate it
    } else {
        screenOrientation = ($(window).width() > $(window).height())? 90 : 0;
    }
});

In a perfect world, that last snippet would work perfectly. Browsers that support window.onorientationchange would be served the value from window.orientation, and browsers that don't would get an emulated value based on their windows' height and width every time it would be resized.

But as you know, we're far from being in a perfect world, and even further from being in a perfect web.

The first issue you'll stumble on is that not every device fires the two above-mentioned events in the same order, nor at the same time. Plus, values they give us don't come in at the same time either. That makes up for quite a mess, and frankly quite a pain to work with as well.

Mobile devices orientationchange / resize firing order

Thanks to two-bit-fool for that table.

The second and main issue is that the orientation of a device is subjective to that very same device.

Galaxy Tab

Galaxy Tab; window.orientation == 0

iPad

iPad; window.orientation == 0

Isn't that amusing. The orientation of both tablets is 0, since they're in their own default position, but one of them is in landscape mode while the other is in portrait mode.

So, let's say good bye to the assumption that orientation == 0 || orientation == 180 means portrait and orientation == 90 || orientation == -90 means landscape. orientation == 0 can imply both of them, as do the other values.

What's the solution then?

Well, what we learned is that results from window.orientation can't be trusted when we only care about whether our device is in portrait or landscape mode.

Remember the little fallback we used earlier to cover cases where window.onorientationchange and window.orientation weren't supported? What about using it all the time?

The window.onresize event is available everywhere, and it's always fired at the right time; never too early, never too late. As a matter of fact, the size of the screen is always accurate as well.

So, instead of using something complicated that doesn't work all the time, let's use something easy and precise, shall we?

// use this
$(window).bind("resize", function(){
    screenOrientation = ($(window).width() > $(window).height())? 90 : 0;
});

That's how you know if and when your screen is in landscape mode or not without any hassle. 90 means landscape, 0 means portrait, and now it's accurate!