'Mobile viewport height after orientation change

I am attaching a listener to the orientationchange event:

window.addEventListener('orientationchange', function () {
    console.log(window.innerHeight);
});

I need to get the height of the document after the orientationchange. However, the event is triggered before the rotation is complete. Therefore, the recorded height reflects the state before the actual orientation change.

How do I register an event that would allow me to capture element dimensions after the orientation change has been completed?



Solution 1:[1]

Use the resize event

The resize event will include the appropriate width and height after an orientationchange, but you do not want to listen for all resize events. Therefore, we add a one-off resize event listener after an orientation change:

Javascript:

window.addEventListener('orientationchange', function() {
    // After orientationchange, add a one-time resize event
    var afterOrientationChange = function() {
        // YOUR POST-ORIENTATION CODE HERE
        // Remove the resize event listener after it has executed
        window.removeEventListener('resize', afterOrientationChange);
    };
    window.addEventListener('resize', afterOrientationChange);
});

jQuery:

$(window).on('orientationchange', function() {
    // After orientationchange, add a one-time resize event
    $(window).one('resize', function() {
        // YOUR POST-ORIENTATION CODE HERE
    });
});

Do NOT use timeouts

Timeouts are unreliable - some devices will fail to capture their orientation change within your hard-coded timeouts; this can be for unforeseen reasons, or because the device is slow. Fast devices will inversely have an unnecessary delay in the code.

Solution 2:[2]

Gajus' and burtelli's solutions are robust but the overhead is high. Here is a slim version that's reasonably fast in 2017, using requestAnimationFrame:

// Wait until innerheight changes, for max 120 frames
function orientationChanged() {
  const timeout = 120;
  return new window.Promise(function(resolve) {
    const go = (i, height0) => {
      window.innerHeight != height0 || i >= timeout ?
        resolve() :
        window.requestAnimationFrame(() => go(i + 1, height0));
    };
    go(0, window.innerHeight);
  });
}

Use it like this:

window.addEventListener('orientationchange', function () {
    orientationChanged().then(function() {
      // Profit
    });
});

Solution 3:[3]

There is no way to capture the end of the orientation change event because handling of the orientation change varies from browser to browser. Drawing a balance between the most reliable and the fastest way to detect the end of orientation change requires racing interval and timeout.

A listener is attached to the orientationchange. Invoking the listener starts an interval. The interval is tracking the state of window.innerWidth and window.innerHeight. The orientationchangeend event is fired when noChangeCountToEnd number of consequent iterations do not detect a value mutation or after noEndTimeout milliseconds, whichever happens first.

var noChangeCountToEnd = 100,
    noEndTimeout = 1000;

window
    .addEventListener('orientationchange', function () {
        var interval,
            timeout,
            end,
            lastInnerWidth,
            lastInnerHeight,
            noChangeCount;

        end = function () {
            clearInterval(interval);
            clearTimeout(timeout);

            interval = null;
            timeout = null;

            // "orientationchangeend"
        };

        interval = setInterval(function () {
            if (global.innerWidth === lastInnerWidth && global.innerHeight === lastInnerHeight) {
                noChangeCount++;

                if (noChangeCount === noChangeCountToEnd) {
                    // The interval resolved the issue first.

                    end();
                }
            } else {
                lastInnerWidth = global.innerWidth;
                lastInnerHeight = global.innerHeight;
                noChangeCount = 0;
            }
        });
        timeout = setTimeout(function () {
            // The timeout happened first.

            end();
        }, noEndTimeout);
    });

I am maintaining an implementation of orientationchangeend that extends upon the above described logic.

Solution 4:[4]

I used the workaround proposed by Gajus Kuizunas for a while which was reliable albeit a bit slow. Thanks, anyway, it did the job!

If you're using Cordova or Phonegap I found a faster solution - just in case someone else faces this problem in the future. This plugin returns the correct width/height values right away: https://github.com/pbakondy/cordova-plugin-screensize

The returned height and width reflect the actual resolution though, so you might have to use window.devicePixelRatio to get viewport pixels. Also the title bar (battery, time etc.) is included in the returned height. I used this callback function initally (onDeviceReady)

var successCallback = function(result){
    var ratio = window.devicePixelRatio;            
    settings.titleBar = result.height/ratio-window.innerHeight; 
    console.log("NEW TITLE BAR HEIGHT: " + settings.titleBar);
};

In your orientation change event handler you can then use:

height = result.height/ratio - settings.titleBar;

to get the innerHeight right away. Hope this helps someone!

Solution 5:[5]

I solved this issue combining a couple of the above solutions. Resize would fire 4-5 times. Using .one, it fired too early. A short time out added to Christopher Bull's solution did the trick.

$(window).on('orientationchange', function () {
  $(window).one('resize', function () {
    setTimeout(reference_to_function, 100);
  });
});

Solution 6:[6]

I also faced the same problem. So I ended up using:

window.onresize = function(){ getHeight(); }

Solution 7:[7]

It is an experimental feature and gives the right innerHeight and innerWidth after screen orientation is changed.

window.screen.orientation.addEventListener('change', function(){
    console.log(window.innerHeight)
})

I think listening to window resize is the safest way to implement screen orientation change logic.

Solution 8:[8]

It is important to note that orientationchange will not get the height after the change, but rather before. Use resize to accomplish this.

$(window).bind('orientationchange', function (e) {
    var windowHeight = $(window).innerHeight();
    console.log('Before Orientation: Height = ' + windowHeight);

    $(window).resize(function () {
        windowHeight = $(window).innerHeight();
        console.log('After Orientation: Height = ' + windowHeight);
    });
});

Solution 9:[9]

In 2022 the following code is supported by all browsers and operating systems:

let counter=1;
let wih = window.innerHeight;
let wiw = window.innerWidth;
let orientationOnLoad;
let mql = window.matchMedia("(orientation: portrait)");
if(mql.matches) {  
    orientationOnLoad='Portrait';
} else {  
    orientationOnLoad='Landscape';
}
console.log('onload: '+orientationOnLoad+', '+wiw+'x'+wih);

mql.addEventListener('change', function(e) {
    let wih = window.innerHeight;
    let wiw = window.innerWidth;
    if(e.matches) {
        console.log('orientationchange event ('+counter+'): Portrait, '+wiw+'x'+wih);
    }
    else {
        console.log('orientationchange event ('+counter+'): Landscape, '+wiw+'x'+wih);
    }
    counter++;
});

window.matchMedia vs screen.orientation

Solution 10:[10]

For people who just want the innerHeight of the window object after the orientationchange there is a simple solution. Use window.innerWidth. The innerWidth during the orientationchange event is the innerHeight after completion of the orientationchange:

window.addEventListener('orientationchange', function () {
    var innerHeightAfterEvent = window.innerWidth;
    console.log(innerHeightAfterEvent);
    var innerWidthAfterEvent = window.innerHeight;
    console.log(innerWidthAfterEvent);
    console.log(screen.orientation.angle);
});

I am not sure if 180-degree changes are done in two 90 degree steps or not. Can't simulate that in the browser with the help of developer tools. But If you want to safeguard against a full 180, 270 or 360 rotation possibility it should be possible to use screen.orientation.angle to calculate the angle and use the correct height and width. The screen.orientation.angle during the event is the target angle of the orientationchange event.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 kizzx2
Solution 3
Solution 4 burtelli
Solution 5
Solution 6 falsarella
Solution 7 sudazzle
Solution 8 Kevin
Solution 9 dsz36
Solution 10 coderuby