CSS Media Query - Soft-keyboard breaks css orientation rules - alternative solution?

55,234

Solution 1

I know this is a couple of years late but I found a great solution

For landscape media:

@media screen and (min-aspect-ratio: 13/9) { /* landscape styles here */}

And for portrait media:

@media screen and (max-aspect-ratio: 13/9) { /* portrait styles here */}

The full solution and why it works can be found here Michael Barret - Android browser challenges

Edit: this link is now expired, but a snapshot can be found on the internet archive: Michael Barret - Android browser challenges

Solution 2

The problem lies in the way that orientation is calculated:

http://www.w3.org/TR/css3-mediaqueries/#orientation

The ‘orientation’ media feature is ‘portrait’ when the value of the ‘height’ media feature is greater than or equal to the value of the ‘width’ media feature. Otherwise ‘orientation’ is ‘landscape’.

Since the height/width is calculated on the visible viewport, the soft keyboard apparently causes the orientation to flip now that the viewport width is less than the height. One solution would be just to use your media queries based on just width instead. This makes it more flexible across devices regardless of orientation, not to mention width/height is more widely supported than orientation.

If you want to account for the width instead of orientation, I'll use the iPhone as an example:

@media screen and (max-width: 320px) {
  /* rules applying to portrait */
}

@media screen and (max-width: 480px) {
  /* rules applying to landscape */
}

This approach is more flexible than orientation since the queries aren't limited to devices/user-agents that support orientation, not to mention that orientation tells you very little versus the width.

Of course if you really need to know orientation, it seems like setting the class initially and just use that might be your best option.

Solution 3

An alternative might be to use device-aspect-ratio, which remains unchanged. However, in Webkit, rotating the device doesn't trigger an update of CSS rules using this query, even though JavaScript tests return true for the new aspect ratio. This is apparently due to a bug, but I'm not able to find a bug report. Using a combination of orientation and {min,max}-device-aspect-ratio seems to work fine:

@media screen and (max-device-aspect-ratio: 1/1) and (orientation: portrait)
@media screen and (min-device-aspect-ratio: 1/1) and (orientation: landscape)

The use of orientation triggers updates as expected, and the use of device-aspect-ratio restricts updated to actual changes of orientation.

Solution 4

I worked through the options listed above and none quite fixed the issues for me. I switched to using screen.availHeight as it gives consistent height results avoiding the keyboard display issue.

// Avoiding the keyboard in Android causing the portrait orientation to change to landscape. 
// Not an issue in IOS. Can use window.orientation.
var currentOrientation = function() {
  // Use screen.availHeight as screen height doesn't change when keyboard displays.  
  if(screen.availHeight > screen.availWidth){
    $("html").addClass('portrait').removeClass('landscape'); 
  } else {
    $("html").addClass('landscape').removeClass('portrait'); 
  }
}

// Set orientation on initiliasation
currentOrientation();
// Reset orientation each time window is resized. Keyboard opening, or change in orientation triggers this.
$(window).on("resize", currentOrientation);

Solution 5

For me, this did the trick:

  @media screen and (max-device-aspect-ratio: 1/1), (max-aspect-ratio: 1/1){
        /*Portrait Mode*/
};

While max-aspect-ratio takes care of triggering Portrait mode simply by resizing the window (useful for PCs and other landscape-only devices), max-device-aspect-ratio helps with smartphones or other devices with variable orientation. And because I'm not declaring specific settings for Landscape mode, even if max-aspect-ratio is reporting >1 to the system, Portrait mode will still be triggered by max-device-aspect-ratio if the Android device is oriented that way.

The 1/1 part basically means that if the ratio is <=1, it goes to Portrait mode, otherwise it goes to landscape mode.

Share:
55,234

Related videos on Youtube

Hossain Khan
Author by

Hossain Khan

Passionate Android engineer, foodie and travel fanatic.

Updated on March 28, 2021

Comments

  • Hossain Khan
    Hossain Khan about 3 years

    I am working with multiple tablet devices - both Android and iOS. Currently I have following resolution variations for all the tablets.

    • 1280 x 800
    • 1280 x 768
    • 1024 x 768 (iPad Obviously) - iPad does not have this issue

    Simplest way to apply device orientation based style is to use media query's orientation using following syntax.

    @media all and (orientation:portrait)
    {
      /* My portrait based CSS here */
    }
    
    @media all and (orientation:landscape)
    {
      /* My landscape based CSS here */
    }
    

    This works perfectly fine on all tablet devices. BUT, the problem is, when device is in portrait mode and user taps on any input field (eg. search) the soft-keyboard pops up - which reduces the visible area of web page and forces it to render in landscape based css. On android tablet devices, it depends on keyboard's height. So, ultimately the web page looks broken. Therefore, I can't use CSS3's orientation media query to apply styles based on orientation (unless there is better media query to target orientation). Here is a fiddle http://jsfiddle.net/hossain/S5nYP/5/ which emulates this - for device testing use full test page - http://jsfiddle.net/S5nYP/embedded/result/

    Here is a screenshot of the behaviour taken from the demo page. enter image description here

    So, is there any alternative to takle this issue, I'm open to JavaScript based solution if native CSS based solution does not work.

    I found a snippet on http://davidbcalhoun.com/2010/dealing-with-device-orientation which suggests to add class on and target based on that. For example:

    <html class="landscape">
      <body>
        <h1 class="landscape-only">Element Heading - Landscape</h1>
        <h1 class="portrait-only">Element Heading - Portrait</h1>
        <!-- .... more... ->
    
    # CSS
    .landscape .landscape-only { display:block; }
    .landspace .portrait-only  { display:none; }
    .portrait .portrait-only   { display:block; }
    .portrait .landscape-only  { display:none; }
    

    What do you guys think about this? Do you have better solution?

    • Hossain Khan
      Hossain Khan over 12 years
      Just found out, iPad does NOT have this issue. ONLY android OS (honeycomb tablets) and mobiles has this issue.
    • TJ Kirchner
      TJ Kirchner almost 12 years
      I ran into a similar issue with my mobile website. After testing across a couple Android devices, it doesn't seem to be limited to any specific OS or mobile/tablet device. It looks like its primarily a problem with Motorola and Samsung devices. I wasn't able to reproduce the issue on our HTC phone.
    • Hossain Khan
      Hossain Khan over 11 years
      This issue is mainly with android devices, mobile/tablet - only way you can reproduce this is to install custom keyboard which has large height, so that, when the keyboard is showing, the available height for webview is less than available width. Only then, webview triggers orientation change. For example, in the screenshot, if I can disable the suggestion layer on top of keyboard, then this issue will not be there, because, webview height will be greater than width. But, anyway, I haven't got any elegant & efficient solution yet.
    • Alexandre
      Alexandre over 10 years
      Just realize that IOS 7.0.0 fullscreen WebApp has this behaviour too
    • Hossain Khan
      Hossain Khan over 10 years
      That's sad. I was not able to resolve the issue efficiently, hence marked it as known limitation.
    • John Conde
      John Conde almost 10 years
  • Hossain Khan
    Hossain Khan over 12 years
    I understand how landscape and portrait is calculated by the browsers. But, need alternative and better way of handling this. Another interesting thing is, iPad does not trigger orientation change when keyboard is up. I don't know why the **** android had to implement that way.
  • scurker
    scurker over 12 years
    I'm just curious as to why you specifically need the orientation of the device as opposed to just the width. What is the problem you're trying to solve that just the width alone won't fix?
  • Hossain Khan
    Hossain Khan over 12 years
    I'm working with multiple device with different resolution. Could you please give example how width can be used as an alternative to orientation? About, use of orientation change, there are multiple application on orientation change - usually available space different in different orientation. So, some elements can be hidden or re-sized based on orientation. Thanks for all the help.
  • RaphaelDDL
    RaphaelDDL over 12 years
    @HossainKhan @media all and (max-device-wdith:NNNpx){} or @media all and (min-device-wdith:NNNpx){} or @media all and (device-wdith:NNNpx){}
  • scurker
    scurker over 12 years
    I updated my answer. The device width should take orientation into account, so that when your orientation changes the width should update accordingly.
  • Ricardo Gomes
    Ricardo Gomes over 11 years
    check out this post on how to use this technique to help define your retina images
  • Robby Jennings
    Robby Jennings over 11 years
    So I've been testing this a bit more and it's not so straight forward. Android devices need to use screen.availHeight and screen.availWidth. IOS is buggy with these though so use window.orientation for IOS. Desktop should use window.innerHeight, window.innerWidth as you want the window dimensions not the screen dimensions.
  • Robby Jennings
    Robby Jennings over 11 years
    I've been bashing my head against the wall with the code above. Too finicky. I'm not supporting desktop for this app, so I'm using window.orientation is the magic solution for Android and IOS. Working fine now. var orientation = Math.abs(window.orientation) === 90 ? 'landscape' : 'portrait';
  • Libin
    Libin almost 11 years
    ultimately by removing and (orientation:portrait) and just applied the max and min width resolved my issue.!!!
  • Stephan Bijzitter
    Stephan Bijzitter about 9 years
    It solves the issue, but creates more issues on other devices for me (mainly laptops and computers)
  • Diogo Cardoso
    Diogo Cardoso about 9 years
    Unfortunately this solution doesn't work in all mobile devices.
  • Sean Routledge
    Sean Routledge almost 9 years
    @StephanBijzitter maybe you could use the 'max-width' property?
  • raacer
    raacer over 8 years
    This solution is very rough and will not work on some devices. For example my phone gives about 17.7/9 when opening the soft keyboard, while the landscape aspect ratio is much less on many other devices.
  • rubmz
    rubmz almost 8 years
    the stupid thing about this whole media query that the browsers actually identify themselves, so y the hell aren't we provided with the information?????
  • William Dunne
    William Dunne over 6 years
    Radical answer dude
  • adi518
    adi518 over 6 years
    What about when the keyboard comes up?
  • helveciofneto
    helveciofneto over 6 years
    When the virtual keyboard comes up on a mobile device, nothing changes, because the device's aspect ratio (max-device-aspect-ratio) only changes when you actually rotate the device or when you deliberately change the screen orientation from Portrait to Landscape and vice-versa.
  • adi518
    adi518 over 6 years
    Yes, I ended up implementing and testing it for myself. I'm developing a package around this: npmjs.com/package/mobile-orientation
  • adi518
    adi518 about 6 years
    Came back to inquire over one more thing: (max-aspect-ratio: 1/1). It seems we only need this bit to detect portrait on desktop?
  • helveciofneto
    helveciofneto about 6 years
    Right. The 1/1 is to ensure that a perfect square still triggers portrait mode (which is possible in systems that allow window resizing, like a browser window on a desktop). Without it, weird things happen in this exact ratio.
  • adi518
    adi518 about 6 years
    I see. I only need to detect mobile, so I don't need this bit. Thanks.
  • adi518
    adi518 about 6 years
    Checking MDN reveals it's deprecated, which is unfortunate. The solution won't last, but can be used as a fallback test.
  • adi518
    adi518 about 6 years
    See here for the npm package I based around it.
  • Sparkmorry
    Sparkmorry about 6 years
    The full solution link is broken.
  • Lars C. Magnusson
    Lars C. Magnusson over 5 years
    Great! While orientation:landscape did not work, this solution did :)
  • Eugene Gluhotorenko
    Eugene Gluhotorenko almost 5 years
    Unfortunately this feature is deprecated now developer.mozilla.org/en-US/docs/Web/CSS/@media/…
  • Amir Farahani
    Amir Farahani almost 4 years
    you save my day :), but when it comes back from landscape to portrait, zoom of the page is broken how to handle this?
  • qraqatit
    qraqatit almost 4 years
    mid 2020 and this still works despite being deprecated...great solution, the only one that actually worked for me in all the cases I have!
  • QMaster
    QMaster over 3 years
    This relies on current max-width of mobile devices and when improving technology these will changes. So should we change all media queries scenario? I think this is a poor idea.
  • QMaster
    QMaster over 3 years
    This relies on current max-width of mobile devices and when improving technology these will changes. So should we change all media queries scenario? I think this is a poor idea.
  • Kelon
    Kelon about 3 years
    This helped me to create my own selector, tested on windows/firefox+chrome, android, iPad: (max-aspect-ratio: 1/1), (orientation: portrait)