Detect virtual keyboard on screen and landscape orientation in javascript

23,696

Solution 1

The short answer:

No, unfortunately there is no current way to detect when the virtual keyboard appears on screen. Detecting orientation yes, that's possible using:

  1. css media queries (as per you example)
  2. via JavaScript using the orientationchange listener.
  3. via JavaScript using the resize listener and inferring the orientation using window.innerHeight > window.innerWidth

The long answer:

There should be no reason why the keyboard affects the orientation property that is interpreted by your css media query as follows:

@media screen and (orientation: landscape) {
    ...
}

The W3C media queries recomendation states the following for the orientation property:

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’.

The keyboard being visible when you type in the forms textbox field simply should not trigger the orientation property to change.

I have in the past had your issue occur when using device emulators, such as Device Mode in older versions of Google Chrome's devtools. However, when it was tested on real mobile device/s the issue did not occur.

Also, there is this bug open in mobile chrome that may be causing resize events to fire when the keyboard appears.

Perhaps the emulator, if that's what you are using, is incorrectly causing the the viewports height to change therefore causing the change to the orientationproperty in your media query.

The simple gist I provided below does the following (You could try running it in your dev environment to see what happens):

  1. When the mobile device is in portrait orientation the screen shows two form input fields (displaying the words: 'foo' and 'baz').
  2. When I touch either of the form input fields the keyboard is displayed and there is no change to the content of the page.
  3. When the device it rotated 90 degrees to landscape orientation a gray panel appears showing the words: "Rotate your device to portrait"

NOTE: The steps listed above are what happens when testing the gist below on several real mobile devices running Safari on iOS and mobile Chrome on Android (...not emulators!).

<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="utf-8" />
    <title>StackOveflow question 40175207</title>
    <meta name="description" content="Example gist using orientation css media query to hsow message when device is landscape" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
<style type="text/css">

body {
    padding: 0;
    margin: 0;
}

.wrapper {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
    padding: 0;
    margin: 0;
}

.message-panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: gray;
    -webkit-transform: translateX(100%);
            transform: translateX(100%);

    will-change: transform;
}

.message-panel__text {
    position: absolute;
    top: 50%;
    text-align: center;
    font: 1em Helvetica, sans-serif;
    width: 100%;
    height: 50px;
    margin: auto;
    -webkit-transform: translateX(-50%);
            transform: translateY(-50%);
}

@media screen and (orientation: landscape) {
    .message-panel {
        transform: translateX(0);
    }
}

</style>

</head>
<body>
    <div class="wrapper">
        <form>
            <input type="text" name="input-one" value="foo">
            <input type="text" name="input-two" value="baz">
        </form>
        <div class="message-panel">
            <div class="message-panel__text">Rotate your device to portrait</div>
        </div>
    </div>
</body>
</html>

PS: There is also Web App Manifest in Chrome for Android which allows you to lock the orientation of the device too. See here for further info.

Hope this helps!

Solution 2

You can't detect whether there is or is not the virtual keyboard only using CSS. You'll need to use some javascript.

If you are using Cordova to build the app, try using the Keyboard plugin. It fires an event whenever keyboard is shown or hidden, so that you can respond to it somehow, ie. add some class to <body> that will prevent showing the message.

https://github.com/driftyco/ionic-plugin-keyboard

Another approach would be to listen to "focus" event on all possible elements that can cause the keyboard to appear.

$(document.body).on("focus", "textarea, input", function() {
   $(document.body).addClass("do-not-show-message");
}).on("blur", "textarea, input", function() {
   $(document.body).removeClass("do-not-show-message");
});;

Solution 3

You can detect focus on input. It is very likely that the device will show a virtual keyboard in the mobile version of your application. To find out, you can compare the height of some element. For example, the application was 800 pixels high, nothing changed (there was no screen flip), but the height changed and now it is 500 pixels. And also you know that the focus is on the input field. This may mean that a virtual keyboard has appeared on the screen. Or not in some cases :)

Solution 4

I had troubles designing a chat view in HTML. Header at top, content center and chat field at the bottom. In the end, this was the solution I opted for

  • Flexbox for the layout
  • Height of the flexbox set to 100vh.
  • Because 100vh is problematic in Safari, detect Safari with js and set to 100%

Here's a fiddle that shows my solution

var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isSafari) {
  document.body.className = "is-safari";
}
html,
body {
  padding: 0;
  margin: 0;
}
.chat {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: stretch;
  align-content: stretch;
  width: 100%;
  height: 100vh;
  background: white;
}
.is-safari .chat {
  height: 100%;
}
.chat__header,
.chat__footer {
  background: whitesmoke;
  height: 60px;
}
.chat__body {
  flex: 1;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Chat View</title>
</head>

<body>
  <div class="chat">
    <div class="chat__header">Header</div>
    <div class="chat__body">Body</div>
    <div class="chat__footer">Footer</div>
  </div>
</body>

</html>

Share:
23,696
Umair Khalid
Author by

Umair Khalid

I’m a Android developer, specializing in native Android apps. Ever since I built my first Android app during the final year of my studies, I have a passion for mobile apps. I create Android apps that follow the Android design guidelines and fit into the Android design ecosystem but with enough breathing room for a distinct branding. I’m interested in all kinds of cool and challenging projects but I’m especially interested in apps that make use of the user’s context (location, activity etc) to provide a magical experience. No matter what your project or idea is though, do get in touch so we can have a chat and see if we’re a fit.

Updated on July 09, 2022

Comments

  • Umair Khalid
    Umair Khalid almost 2 years

    Am working on a html based mobile app where there is requirement to design app only for portrait orientation and display message to user when he goes to landscape, This feature is working fine with css media query but problem is this that I have a form in app and when I click on any of the textbox and virtual keyboard opens and screen height changes and it shows me message for orientation change

    Now I want to ask if there any way to different when actually orientation changes and when virtual keyboard on screen appears. Thanks in advance

    @media screen and (orientation: landscape) {
    
    }
    

    I have checked other questions related to my topic on stackoverflow but nothing helpful there

  • Gabriel Lupu
    Gabriel Lupu over 2 years
    So basically your solution works sometimes and sometimes doesn't?
  • Ilay Vasilyev
    Ilay Vasilyev over 2 years
    Right. I think it will work 90 percent, and maybe more.