JavaScript
Article

Introducing the Screen Orientation API

By Aurelio De Rosa

Everyone uses the web in a different way. There are people who use it to chat, others use it to watch movies, and others use it to play games. For those of you who access the internet to play games on their mobile device, I have a question: have you ever played a game that asks you to rotate the screen to start? I bet the answer is yes.

The reason you have to rotate the screen is that the UI has been developed for a specific mode, portrait or landscape. If your web application has the same need, or you are developing one of these games, I have a JavaScript API for you: the Screen Orientation API. In this article I’ll discuss what it is and what it is good for.

What is the Screen Orientation API?

The Screen Orientation API provides the ability to read the screen orientation state, to be informed when this state changes, and to be able to lock the screen orientation to a specific state. In other words, you are now able to detect the orientation of a user’s device (in terms of portrait and landscape) and lock in the mode you need. Doing so, you don’t need to show your users weird animations and labels to specify the orientation needed. In fact, you can now set the device’s screen in the orientation needed so that the user will understand how to rotate the device.

The Screen Orientation API is at a very early stage, as it’s a W3C Working Draft. The current specifications may be superseded in a few months by a newer version that is currently in progress. The new version is slightly different from the old one because it introduces a new property and a different return type for one of the methods.

It’s also worth noting that to lock the screen, the web page must be in full-screen mode (you can achieve this using the Fullscreen API).

Now that you know what the Screen Orientation API is, let’s discover more about its properties and methods.

Properties and Events

The Screen Orientation API extends the screen object of window with a property, orientation, and two methods, lockOrientation() and unlockOrientation().

The orientation property returns a string representing the orientation of the screen. Its value can be one of the followings:

  • portrait-primary: The orientation is in the primary portrait mode. For a smartphone this values means that it’s in a vertical position with the buttons at the bottom.
  • portrait-secondary: The orientation is in the secondary portrait mode. For a smartphone this value means that it’s in a vertical position with the buttons at the top (the device is down under)
  • landscape-primary: The orientation is in the primary landscape mode. For a smartphone this value means that it’s in a horizontal position with the buttons at the right.
  • landscape-secondary: The orientation is in the secondary landscape mode. For a smartphone this value means that it’s in a horizontal position with the buttons at the left.

The lockOrientation() and unlockOrientation() methods are complementary in what they do. The first method locks the device’s screen as if the device was physically rotated in a certain position, as shown in the figure below. It returns a Boolean which is true in case of success of the operation and false otherwise.

Device Orientation API in Action

The Screen Orientation API in action. The device is physically in portrait mode but the screen acts as if it was in landscape.

lockOrientation() accepts one or more strings to specify the orientations we want to lock the screen at. The strings must be one of the previously mentioned values of the orientation property, portrait to specify the orientation can be either portrait-primary and portrait-secondary, or landscape to indicate the orientation can be either landscape-primary and landscape-secondary.

An example of code that uses this method and passes one string is shown below:

window.screen.lockOrientation('portrait');

Here is an example that passes more than one string:

window.screen.lockOrientation('portrait-primary', 'landscape-primary');

The unlockOrientation() method is used to release a previously set lock and allows the screen to rotate again in every position.

The API also provides an event named onorientationchange that is fired every time the screen orientation changes. We can use this event to detect changes of the orientation and update the UI or the business logic of our website accordingly.

Browser Compatibility

The support for the Screen Orientation API is very good, although some browsers still use the prefixed version. The API is supported by Firefox 18+ using its vendor prefix (moz), Internet Explorer 11+ using its vendor prefix (ms), Chrome 38+ (currently in beta), and Opera 25+ (currently in beta). Unfortunately, just like many other really interesting and useful APIs, Safari doesn’t have any support.

As you can see, with the next release of Chrome and Opera, you’ll have almost every major browser supporting this API. So, you can really use it in your next project. Even without support, we’ve learned to develop our projects based on feature detection. To test if the API is implemented your browser, you can write:

if ('orientation' in screen) {
   // API supported, yeah!
} else {
   // API not supported :(
}

So far, we’ve covered the properties and the events exposed by the API as well as the API’s use cases. In the next section, we’re going to create a simple web page to see the Screen Orientation API in action.

Demo

The demo we’re going to develop consists of an HTML page that displays text indicating the current orientation of the screen. Then, we have a select box to specify the orientation we want to lock the screen at. Finally, we have two buttons: one to lock the screen and the other to unlock it.

Inside the JavaScript code we detect if the browser supports this API or not, and what prefix it uses, if any. If the API isn’t implemented in the browser we display the message “API not supported” and the buttons will be disabled. If the API is supported we attach a handler to the correct event (the name varies because of the prefixes) where we update the text of the paragraph to show the current orientation.

Finally, we create other two handlers. In the first we set the page in full-screen mode and lock the screen in the orientation specified by the select box. In the second we remove the lock and exit full-screen mode.

Important note: While developing and testing this demo I’ve found a couple of bugs in Firefox. The browser crashes with any value passed to lockOrientation() (actually mozLockOrientation()) but portrait-primary and portrait-secondary. In addition, when portrait-secondary is given, Firefox acts as if the string was landscape-primary. Based on my tweets, two bugs have been filed (https://bugzil.la/1061372 and https://bugzil.la/1061373) and hopefully they will be fixed soon.

You can find the complete code for the demo below. You can also play with it online if you like.

<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
      <meta name="author" content="Aurelio De Rosa">
      <title>Screen Orientation API Demo by Aurelio De Rosa</title>
      <style>
         *
         {
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            box-sizing: border-box;
         }

         body
         {
            max-width: 500px;
            margin: 2em auto;
            padding: 0 0.5em;
            font-size: 20px;
         }

         h1
         {
            text-align: center;
         }

         .api-support
         {
            display: block;
         }

         .hidden
         {
            display: none;
         }

         .value
         {
            font-weight: bold;
         }

         .button-demo
         {
            padding: 0.5em;
            margin: 1em;
         }

         .author
         {
            display: block;
            margin-top: 1em;
         }
      </style>
   </head>
   <body>
      <h1>Screen Orientation API</h1>

      <span id="so-unsupported" class="api-support hidden">API not supported</span>

      <div id="so-results">
         <p>
            The orientation of the device is <span id="orientation" class="value">unavailable</span>
         </p>
         <form id="form-orientation">
            <label for="orientation">Lock the device in:</label>
            <select id="orientation-type">
               <option value="portrait">portrait</option>
               <option value="landscape">landscape</option>
               <option value="portrait-primary">portrait-primary</option>
               <option value="portrait-secondary">portrait-secondary</option>
               <option value="landscape-primary">landscape-primary</option>
               <option value="landscape-secondary">landscape-secondary</option>
            </select>
            <br />
            <input class="button-demo" id="lock-button" type="submit" value="Lock!" />
            <button class="button-demo" id="unlock-button">Unlock!</button>
         </form>
      </div>

      <small class="author">
         Demo created by <a href="http://www.audero.it">Aurelio De Rosa</a>
         (<a href="https://twitter.com/AurelioDeRosa">@AurelioDeRosa</a>).<br />
         This demo is part of the <a href="https://github.com/AurelioDeRosa/HTML5-API-demos">HTML5 API demos repository</a>.
      </small>

      <script>
         var prefix = 'orientation' in screen ? '' :
                      'mozOrientation' in screen ? 'moz' :
                      'msOrientation' in screen ? 'ms' :
                      null;

         if (prefix === null) {
            document.getElementById('so-unsupported').classList.remove('hidden');

            ['lock-button', 'unlock-button'].forEach(function(elementId) {
               document.getElementById(elementId).setAttribute('disabled', 'disabled');
            });
         } else {
            var form = document.getElementById('form-orientation');
            var select = document.getElementById('orientation-type');

            // Function needed to see lock in action
            function launchFullscreen(element) {
               if(element.requestFullscreen) {
                  element.requestFullscreen();
               } else if(element.mozRequestFullScreen) {
                  element.mozRequestFullScreen();
               } else if(element.webkitRequestFullscreen) {
                  element.webkitRequestFullscreen();
               } else if(element.msRequestFullscreen) {
                  element.msRequestFullscreen();
               }
            }

            function exitFullscreen() {
               if(document.exitFullscreen) {
                  document.exitFullscreen();
               } else if(document.mozCancelFullScreen) {
                  document.mozCancelFullScreen();
               } else if(document.webkitExitFullscreen) {
                  document.webkitExitFullscreen();
               } else if (document.msExitFullscreen) {
                  document.msExitFullscreen();
               }
            }

            function orientationHandler() {
               var orientationProperty = prefix + (prefix === '' ? 'o' : 'O') + 'rientation';
               document.getElementById('orientation').textContent = screen[orientationProperty];
            }

            screen.addEventListener(prefix + 'orientationchange', orientationHandler);
            document.getElementById('lock-button').addEventListener('click', function(event) {
               event.preventDefault();
               launchFullscreen(document.documentElement);

               setTimeout(function() {
                  screen[prefix + (prefix === '' ? 'l' : 'L') + 'ockOrientation'](select.value);
               }, 1);
            });
            document.getElementById('unlock-button').addEventListener('click', function() {
               exitFullscreen();
               screen[prefix + (prefix === '' ? 'u' : 'U') + 'nlockOrientation']();
            });

            orientationHandler();
         }
      </script>
   </body>
</html>

Conclusion

In this article we’ve discussed the Screen Orientation API. This API exposes one property that specifies the orientation of the screen and two methods. The first method allows you to lock the screen in a given orientation, while the second releases the lock. Finally, it provides an event to know when a change of orientation happens.

As you’ve seen, using this API is quite simple and it should not be too difficult for you to employ it in a future project. Finally, the support among browsers is very good, so this is really something you can plan to adopt.

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

  • Craig Buckler

    Interesting. Given the current bugs, I’m wondering if we could emulate it using matchMedia to respond to orientation changes then transform the page by 90 degrees if locking was required?

  • http://rafaelmayrink.com/ Rafael Mayrink

    Very good the article! Tested in FIREFOX mobile version 32.0.3, bug bug bug.

  • http://smus.com Boris Smus

    Heads up, your demo link is not loading: http://aurelio.audero.it/demo/screen-orientation-api-demo.html

  • adam__roberts

    It’s back up now, thanks Boris!

  • https://www.linkedin.com/in/rahuldesai1 Rahul Desai

    Not completely relevant to this article but the Screen Orientation API doesnt work as expected in Firefox OS. If I lock the orientation in my app and suppose I call the Mail app using Web Activity and send a mail and come back to my app, the orientation is unlocked. Is there something that watches the orientation change globally? I tried with screen.addEventListener(“orientationchange”, … but it didnt work at all.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in JavaScript, once a week, for free.