javascript max viewport height after orientation change Android & iOS

17,993

This is a simple solution that will append the browsers width and height to the document body on load and window resize.

jQuery.event.add(window, "load", resize);
jQuery.event.add(window, "resize", resize);

  function resize() 
    {
      var h = jQuery(window).height();
      var w = jQuery(window).width();
      jQuery("body").css({"width": w, "height": h});
    }
Share:
17,993
Dan Kanze
Author by

Dan Kanze

This site is evil. https://kanze.co

Updated on June 17, 2022

Comments

  • Dan Kanze
    Dan Kanze almost 2 years

    The Goal:

    To find the max viewport height of a device including the space of the address bar so that we can dynamically resize the min-body and push our content up.

    The Problem:

    Mobile browsers handle orientation states differently and update DOM properties on orientation change differently.

    Detect rotation of Android phone in the browser with JavaScript

    With Android phones, screen.width or screen.height also updates as the device is rotated.

    |==============================================================================|
    |     Device     | Events Fired      | orientation | innerWidth | screen.width |
    |==============================================================================|
    | iPad 2         | resize            | 0           | 1024       | 768          |
    | (to landscape) | orientationchange | 90          | 1024       | 768          |
    |----------------+-------------------+-------------+------------+--------------|
    | iPad 2         | resize            | 90          | 768        | 768          |
    | (to portrait)  | orientationchange | 0           | 768        | 768          |
    |----------------+-------------------+-------------+------------+--------------|
    | iPhone 4       | resize            | 0           | 480        | 320          |
    | (to landscape) | orientationchange | 90          | 480        | 320          |
    |----------------+-------------------+-------------+------------+--------------|
    | iPhone 4       | resize            | 90          | 320        | 320          |
    | (to portrait)  | orientationchange | 0           | 320        | 320          |
    |----------------+-------------------+-------------+------------+--------------|
    | Droid phone    | orientationchange | 90          | 320        | 320          |
    | (to landscape) | resize            | 90          | 569        | 569          |
    |----------------+-------------------+-------------+------------+--------------|
    | Droid phone    | orientationchange | 0           | 569        | 569          |
    | (to portrait)  | resize            | 0           | 320        | 320          |
    

    Because of this it is clear that to find the max viewport height no matter what orientation, using a single function to return the max height of a device will never be constant over a range of devices.

    Other problems I have discovered that don't make these two play nice:

    • The window.devicePixelRatio property can return inconsistent heights when dividing by window.outerHeight.
    • Delay window.setTimeout(function() {}, time) needs to be used to give DOM elements a chance to update after orientation change.
    • window.outerHeight is not updated on orientation changes for iOS devices. Using screen.availHeight as a fallback includes the bottom nav bar as total height.
    • Using a #header, #content, #footer structure forces you to dynamically recalculate the #content{min-height} to push the #footer down when the body is dyamically updated.

    A Solution:

    First let's take a look at DIV structure:

    <style>
    #header,#content,#footer{width:100%;}
    </style>
    
    <body>
    <div id="header"></div>
    <div id="content"></div>
    <div id="footer"></div>
    </body>
    

    We want to prevent devices from scaling on their own:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    

    We need help to have the ability to return a max viewport height and hide address bar for iOS:

    <script src="iOS.js" type="text/javascript"></script>
    

    http://iosjs.com/

    Then detect if the device supports orientation change and use resize as a fallback:

    var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/i) ? true : false);
    var android = (navigator.userAgent.match(/Android/i) ? true : false);
    var supportsOrientationChange = "onorientationchange" in window;
    var orientationEvent = supportsOrientationChange ? "orientationchange" : "resize"; 
    

    The belly of the beast:

    function updateOrientation()
    {
        var orientation = (window.orientation);
    
        if(android)
        {
            window.setTimeout(function() {
                window.scrollTo(0,0);
                var size = window.outerHeight/window.devicePixelRatio;
                $('body').css('min-height', size + 'px');
                var headerHeight = $('#header').height();
                var footerHeight = $('#footer').height();
                var contentHeight = size - (headerHeight+footerHeight);
                $('#content').css('min-height', contentHeight + 'px');
                window.scrollTo(0,1);
            }, 200);
        }
    
        if(iOS)
        {
            window.setTimeout(function(){
                window.scrollTo(0,0);
                var size = iOS_getViewportSize();
                var headerHeight = $('#header').height();
                var footerHeight = $('#footer').height();
                var contentHeight = size.height - (headerHeight+footerHeight);
                $('#content').css('min-height', contentHeight + 'px');
                window.scrollTo(0,1);
            }, 0);
        }
    }
    

    Add event listeners for page load and orientation event:

    if(iOS)
    {
        iOS_addEventListener(window, "load", iOS_handleWindowLoad);
        iOS_addEventListener(window, "orientationchange", iOS_handleOrientationChange);
        iOS_addEventListener(window, "resize", iOS_handleReize);
    }
    addEventListener("load", function() 
    {
        updateOrientation();
    }, false);
    addEventListener(orientationEvent, function() {
        updateOrientation();
    }, false);
    

    Proof is in the pudding:

    iPhone 4 & 4s Portrait & Landscape

    iPhone 4 & 4s Portrait iPhone 4 & 4s Landscape


    Android Portrait & Landscape

    Android Portrait Android Landscape


    The goal here is to minify this solution or make it better.

  • Dan Kanze
    Dan Kanze over 11 years
    I see what you are doing here. Although, using if (CurrentOrientation && CurrentOrientation !== NewOrientation) is not allowing the ability for a user to pull height after orientation change in any state. For example, I view landscape I am returned 230px / I view portrait I am returned 450px. Is there a modification that you could make to this that would support that?
  • frenchie
    frenchie over 11 years
    Well then it's an easy fix: what's the name of the function that's returning 230 and 450?
  • Dan Kanze
    Dan Kanze over 11 years
    I suppose what I'm saying is that I need a function to return the viewport height on orientation change. For example, with an iphone, I would expect to be returned 230px at landscape, and 450px at portrait. var h = (window.screen.availHeight/window.devicePixelRatio); alert(h);
  • frenchie
    frenchie over 11 years
    See edit, just updated it with window.screen.availHeight/window.devicePixelRatio
  • Dan Kanze
    Dan Kanze over 11 years
    I'm sorry but this doesn't properly calculate the max viewport height for either Android or iPhone.
  • frenchie
    frenchie over 11 years
    You just put that (window.screen.availHeight/window.devicePixelRatio); returns 230px or 450px!! So I took your function and modified the code accordingly to make it work with that function. Anyway, when you get the function's name and syntax, just replace it with the line (window.screen.availHeight/window.devicePixelRatio); and it'll work. Happy coding.
  • frenchie
    frenchie over 11 years
    Note, I changed it to work with jQuery; if I were you, I'd try that and see what you get (assuming jQuery is already on your page).
  • Dan Kanze
    Dan Kanze over 11 years
    Have you tried this yourself? I'm looking at this with both a iPhone and Android in front of me and it doesnt seem to return the results im looking for..I'm sorry if the question was unclear but im tyring to find the max viewport height of a device. The code I provided was mearly to provoke the idea of what I am trying to acheive. As mentioned above, the window.screen.availHeight does not work for iPhone.
  • Dan Kanze
    Dan Kanze over 11 years
    is this a "simulator" or an emulator? "simulators" will not properly replicate this problem so i am sorry about that.
  • frenchie
    frenchie over 11 years
    No, I don't have an iPhone or Android to try it on. You are solving two issues: 1) determining the orientation's height and 2) detecting orientation changes. My solution does solve problem 2) and all you need to find is one line to get the value. Did you try with the jquery line?
  • Dawson
    Dawson over 11 years
    It's the 'iOS Simulator' that came with my Mac
  • Dan Kanze
    Dan Kanze over 11 years
    I wish it was this easy. The iPhone is buggy in updating DOM element properties after orientation change. The real problem is overcoming this obsticle. Testing with an iPhone in front of you seems like the only real way to replicate the issue I describe here. If the iPhone didn't have this issue I would also expect your logic to work.
  • Dan Kanze
    Dan Kanze over 11 years
    Does your iOS simulator invoke mobile safari with the same hardware constraints? Im sorry but simulators do not replicate this problem the way the actual device does.
  • frenchie
    frenchie over 11 years
    I just thought of something that could do the trick. At the top of the body, you put a div with no content; give it ID WidthCheck and CSS {clear: both; height: 1px; background: transparent;} This will draw a line that spans the whole width. And then, every second, we're going to check the width of this div: if it's over 350px wide then we're on landscape and if it's less then wouldn't that mean we're on portrait? I think that could solve it. All you'd need to do is check for the width of that div. See update.
  • Dan Kanze
    Dan Kanze over 11 years
    You are assuming the width of the portrait is equal to the height of the landscape correct?
  • Dawson
    Dawson over 11 years
    Yes - Mobile Safari. It is the simulator that comes with Xcode, "the complete developer toolset for building Mac, iPhone, and iPad apps, including the Xcode IDE, Instruments, and iOS Simulator" <-- developer.apple.com/technologies/ios
  • frenchie
    frenchie over 11 years
    No, if we're just looking to determine if we've changed orientation, we don't care about the height. If we can determine that the width changed, then wouldn't that mean that the height also changed, and that therefore the orientation changed?
  • BoltClock
    BoltClock over 11 years
    It does not come with the same hardware constraints, but it does respond correctly to media queries and other environment variables the same way an actual device does.
  • Dan Kanze
    Dan Kanze over 11 years
    It may respond the same but in testing the method does not return the correct height for each orientation.