检测JavaScript中屏幕上的虚拟键盘和横向方向

16

我正在开发一个基于html的移动应用,在这个应用中,有一个要求只在纵向模式下设计应用,并在用户切换到横向模式时向其显示消息。这个功能通过css媒体查询可以很好地实现,但问题是应用中有一个表单,当我点击任何文本框并打开虚拟键盘时,屏幕高度会改变,此时它会向我显示方向更改的消息。

现在我想问的是,是否有办法区分实际方向更改和虚拟键盘出现导致的屏幕高度更改。

谢谢!

@media screen and (orientation: landscape) {

}

我在stackoverflow上查看了与我的主题相关的其他问题,但没有找到有用的内容。

5个回答

7
你无法仅使用CSS来检测虚拟键盘是否存在。你需要使用一些JavaScript。
如果你正在使用Cordova构建应用程序,请尝试使用Keyboard插件。它会在键盘显示或隐藏时触发事件,以便你可以对其进行响应,例如向添加一些类,以防止显示消息。
另一种方法是监听可能导致键盘出现的所有可能元素上的"focus"事件。 https://github.com/driftyco/ionic-plugin-keyboard
$(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");
});;

6

简短的回答:

很遗憾,目前没有办法检测虚拟键盘何时出现。但是可以通过以下方式检测屏幕方向:

  1. 使用CSS媒体查询(如您的示例)
  2. 使用JavaScript监听orientationchange事件。
  3. 使用JavaScript监听resize事件,并通过window.innerHeight > window.innerWidth来推断方向。

详细的回答:

理论上讲,虚拟键盘不应该影响由CSS媒体查询解释的orientation属性。

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

W3C的媒体查询推荐中针对orientation属性的说明如下:

当‘height’媒体特性的值大于或等于‘width’媒体特性的值时,‘orientation’媒体特性为‘portrait’。否则,‘orientation’为‘landscape’。

在表单文本框字段中输入时,键盘出现不应触发orientation属性更改。

过去,在使用设备仿真器(例如Google Chrome devtools旧版本中的Device Mode)时,我曾遇到过您的问题。然而,当在真实移动设备上进行测试时,该问题并未发生。

此外,移动版Chrome中存在错误,可能会导致键盘出现时触发调整大小事件。

也许如果您正在使用仿真器,则会错误地导致视口高度发生变化,从而导致在媒体查询中更改orientation属性。

我提供的简单要点如下(您可以在开发环境中运行它以查看发生了什么):
  1. 当移动设备处于纵向方向时,屏幕显示两个表单输入字段(显示单词:“foo”和“baz”)。
  2. 当我触摸任一表单输入字段时,键盘会显示,页面内容不会更改。
  3. 当设备旋转90度到横向方向时,会出现一个灰色面板,显示单词:“将设备旋转至纵向方向”
注意:上述步骤是在几台真实移动设备上测试下面的要点时发生的,这些设备运行着iOS上的Safari和Android上的移动Chrome(...不是模拟器!)。
<!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:在Chrome for Android中还有Web应用程序清单,可以锁定设备的方向。有关更多信息,请参见此处

希望这能帮到您!


3

您可以检测输入框的焦点状态。 在您的应用程序的移动版本中,设备很可能会显示一个虚拟键盘。您可以通过比较某个元素的高度来确定是否显示了虚拟键盘。例如,应用程序高度为800像素,没有发生任何屏幕翻转,但是高度变为500像素。同时您还可以知道焦点在输入框上。这可能意味着屏幕上出现了虚拟键盘。或者,在某些情况下可能没有出现 :)


所以基本上你的解决方案有时候有效,有时候无效? - Gabriel Lupu
2
没错。我认为它会有90%的成功率,甚至更高。 - Ilay Vasilyev

2

我在HTML中设计聊天视图时遇到了麻烦:头部在顶部,内容居中,聊天框在底部。最终,我选择了以下解决方案:

  • 使用Flexbox进行布局
  • 将flexbox的高度设置为100vh。
  • 由于100vh在Safari中存在问题,因此使用js检测Safari并将其设置为100%。

以下是演示我的解决方案的fiddle:

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>


1
我使用下面的代码来检测虚拟键盘是否被显示。我假设两件事情:
  1. 键盘高度超过50像素,这有助于忽略手动调整屏幕大小的情况。
  2. 在键盘显示时,宽度不会改变。这避免了捕捉到用户旋转其屏幕的情况。
基本上,当页面加载时,它保存高度和宽度。您可以在此处进行额外的检查,以确保页面没有在横向加载。之后,任何调整大小的事件都会运行一个检查。如果高度减少了50像素或更多,而宽度保持不变,则可能显示了键盘!如果高度增加了50像素以上,而宽度保持不变,则键盘已隐藏。
window.onload = WindowOnload;
window.onresize = WindowResize;

function WindowOnload() {
    document.body.setAttribute("height", window.innerHeight);
    document.body.setAttribute("width", window.innerWidth);
    if(window.innerHeight < window.innerWidth) { /* Page loaded in landscape */ }
}

function WindowResize() {
    const previousWidth = parseInt(document.body.getAttribute("width"));
    const newWidth = window.innerWidth;
    const previousHeight = parseInt(document.body.getAttribute("height"));
    const newHeight = window.innerHeight;

    if(previousWidth === newWidth) {
        if(previousHeight > newHeight+50) { /* Keyboard was displayed */ console.log("shown"); }
        else if (previousHeight+50 < newHeight) { /* Keyboard was hidden */ console.log("hidden"); }
    }

    document.body.setAttribute("height", window.innerHeight);
    document.body.setAttribute("width", window.innerWidth);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接