iPad Web 应用程序:在 Safari 中使用 JavaScript 检测虚拟键盘?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2593139/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:03:55  来源:igfitidea点击:

iPad Web App: Detect Virtual Keyboard Using JavaScript in Safari?

javascriptiphoneipadsafariweb-applications

提问by LKM

I'm writing a web app for the iPad (not a regular App Store app- it's written using HTML, CSS and JavaScript). Since the keyboard fills up a huge part of the screen, it would make sense to change the app's layout to fit the remaining space when the keyboard is shown. However, I have found no way to detect when or whether the keyboard is shown.

我正在为 iPad 编写一个网络应用程序(不是一个普通的 App Store 应用程序——它是使用 HTML、CSS 和 JavaScript 编写的)。由于键盘占据了屏幕的很大一部分,因此在显示键盘时更改应用程序的布局以适应剩余空间是有意义的。但是,我发现无法检测何时或是否显示键盘。

My first idea was to assume that the keyboard is visible when a text field has focus. However, when an external keyboard is attached to an iPad, the virtual keyboard does not show up when a text field receives focus.

我的第一个想法是假设当文本字段具有焦点时键盘是可见的。但是,当外部键盘连接到 iPad 时,当文本字段获得焦点时,虚拟键盘不会显示。

In my experiments, the keyboard also did not affect the height or scrollheight of any of the DOM elements, and I have found no proprietary events or properties which indicate whether the keyboard is visible.

在我的实验中,键盘也没有影响任何 DOM 元素的高度或滚动高度,而且我没有发现指示键盘是否可见的专有事件或属性。

采纳答案by LKM

I found a solution which works, although it is a bit ugly. It also won't work in every situation, but it works for me. Since I'm adapting the size of the user interface to the iPad's window size, the user is normally unable to scroll. In other words, if I set the window's scrollTop, it will remain at 0.

我找到了一个有效的解决方案,虽然它有点难看。它也不适用于所有情况,但对我有用。由于我将用户界面的大小调整为 iPad 的窗口大小,因此用户通常无法滚动。换句话说,如果我设置了窗口的 scrollTop,它将保持为 0。

If, on the other hand, the keyboard is shown, scrolling suddenly works. So I can set scrollTop, immediately test its value, and then reset it. Here's how that might look in code, using jQuery:

另一方面,如果显示键盘,滚动会突然起作用。所以我可以设置scrollTop,立即测试它的值,然后重置它。以下是使用 jQuery 的代码中的样子:

$(document).ready(function(){
    $('input').bind('focus',function() {
        $(window).scrollTop(10);
        var keyboard_shown = $(window).scrollTop() > 0;
        $(window).scrollTop(0);

        $('#test').append(keyboard_shown?'keyboard ':'nokeyboard ');
    });
});

Normally, you would expect this to not be visible to the user. Unfortunately, at least when running in the Simulator, the iPad visibly (though quickly) scrolls up and down again. Still, it works, at least in some specific situations.

通常,您希望这对用户不可见。不幸的是,至少在模拟器中运行时,iPad 明显(虽然很快)再次上下滚动。尽管如此,它仍然有效,至少在某些特定情况下是这样。

I've tested this on an iPad, and it seems to work fine.

我已经在 iPad 上测试过了,它似乎工作正常。

回答by Per Quested Aronsson

You can use the focusoutevent to detect keyboard dismissal. It's like blur, but bubbles. It will fire when the keyboard closes (but also in other cases, of course). In Safari and Chrome the event can only be registered with addEventListener, not with legacy methods. Here is an example I used to restore a Phonegap app after keyboard dismissal.

您可以使用focusout事件来检测键盘关闭。这就像模糊,但气泡。它会在键盘关闭时触发(当然在其他情况下也是如此)。在 Safari 和 Chrome 中,事件只能使用 addEventListener 注册,而不能使用旧方法注册。这是我用来在键盘关闭后恢复 Phonegap 应用程序的示例。

 document.addEventListener('focusout', function(e) {window.scrollTo(0, 0)});

Without this snippet, the app container stayed in the up-scrolled position until page refresh.

如果没有此代码段,应用程序容器将停留在向上滚动位置,直到页面刷新。

回答by Michele

maybe a slightly better solution is to bind (with jQuery in my case) the "blur" event on the various input fields.

也许更好的解决方案是在各种输入字段上绑定(在我的情况下使用 jQuery)“模糊”事件。

This because when the keyboard disappear all form fields are blurred. So for my situation this snipped solved the problem.

这是因为当键盘消失时,所有表单域都变得模糊了。所以对于我的情况,这个剪辑解决了这个问题。

$('input, textarea').bind('blur', function(e) {

       // Keyboard disappeared
       window.scrollTo(0, 1);

});

hope it helps. Michele

希望能帮助到你。米歇尔

回答by ianh

If there is an on-screen keyboard, focusing a text field that is near the bottom of the viewport will cause Safari to scroll the text field into view. There might be some way to exploit this phenomenon to detect the presence of the keyboard (having a tiny text field at the bottom of the page which gains focus momentarily, or something like that).

如果有屏幕键盘,聚焦靠近视口底部的文本字段将导致 Safari 将文本字段滚动到视图中。可能有一些方法可以利用这种现象来检测键盘的存在(页面底部有一个微小的文本字段,可以暂时获得焦点,或者类似的东西)。

回答by Hafthor

During the focus event you can scroll past the document height and magically the window.innerHeight is reduced by the height of the virtual keyboard. Note that the size of the virtual keyboard is different for landscape vs. portrait orientations so you'll need to redetect it when it changes. I would advise against remembering these values as the user could connect/disconnect a bluetooth keyboard at any time.

在焦点事件期间,您可以滚动超过文档高度,并且 window.innerHeight 会神奇地减少虚拟键盘的高度。请注意,横向和纵向的虚拟键盘大小不同,因此您需要在它发生变化时重新检测它。我建议不要记住这些值,因为用户可以随时连接/断开蓝牙键盘。

var element = document.getElementById("element"); // the input field
var focused = false;

var virtualKeyboardHeight = function () {
    var sx = document.body.scrollLeft, sy = document.body.scrollTop;
    var naturalHeight = window.innerHeight;
    window.scrollTo(sx, document.body.scrollHeight);
    var keyboardHeight = naturalHeight - window.innerHeight;
    window.scrollTo(sx, sy);
    return keyboardHeight;
};

element.onfocus = function () {
    focused = true;
    setTimeout(function() { 
        element.value = "keyboardHeight = " + virtualKeyboardHeight() 
    }, 1); // to allow for orientation scrolling
};

window.onresize = function () {
    if (focused) {
        element.value = "keyboardHeight = " + virtualKeyboardHeight();
    }
};

element.onblur = function () {
    focused = false;
};

Note that when the user is using a bluetooth keyboard, the keyboardHeight is 44 which is the height of the [previous][next] toolbar.

请注意,当用户使用蓝牙键盘时,keyboardHeight 为 44,即 [previous][next] 工具栏的高度。

There is a tiny bit of flicker when you do this detection, but it doesn't seem possible to avoid it.

进行此检测时会出现一点点闪烁,但似乎无法避免。

回答by robocat

Edit: Documented by Apple although I couldn't actually get it to work: WKWebView Behavior with Keyboard Displays: "In iOS 10, WKWebView objects match Safari's native behavior by updating their window.innerHeight property when the keyboard is shown, and do not call resize events" (perhaps can use focus or focus plus delay to detect keyboard instead of using resize).

编辑:由 Apple 记录,虽然我实际上无法让它工作:WKWebView Behavior with Keyboard Displays:“在 iOS 10 中,WKWebView 对象通过在显示键盘时更新它们的 window.innerHeight 属性来匹配 Safari 的本机行为,并且不调用调整大小事件”(也许可以使用焦点或焦点加延迟来检测键盘而不是使用调整大小)。

Edit: code presumes onscreen keyboard, not external keyboard. Leaving it because info may be useful to others that only care about onscreen keyboards. Use http://jsbin.com/AbimiQup/4to view page params.

编辑:代码假定屏幕键盘,而不是外部键盘。离开它是因为信息可能对只关心屏幕键盘的其他人有用。使用http://jsbin.com/AbimiQup/4查看页面参数。

We test to see if the document.activeElementis an element which shows the keyboard (input type=text, textarea, etc).

我们测试是否document.activeElement是显示键盘的元素(输入类型=文本、文本区域等)。

The following code fudges things for our purposes (although not generally correct).

以下代码为我们的目的捏造了一些东西(尽管通常不正确)。

function getViewport() {
    if (window.visualViewport && /Android/.test(navigator.userAgent)) {
        // https://developers.google.com/web/updates/2017/09/visual-viewport-api    Note on desktop Chrome the viewport subtracts scrollbar widths so is not same as window.innerWidth/innerHeight
        return {
            left: visualViewport.pageLeft,
            top: visualViewport.pageTop,
            width: visualViewport.width,
            height: visualViewport.height
        };
    }
    var viewport = {
            left: window.pageXOffset,   // http://www.quirksmode.org/mobile/tableViewport.html
            top: window.pageYOffset,
            width: window.innerWidth || documentElement.clientWidth,
            height: window.innerHeight || documentElement.clientHeight
    };
    if (/iPod|iPhone|iPad/.test(navigator.platform) && isInput(document.activeElement)) {       // iOS *lies* about viewport size when keyboard is visible. See http://stackoverflow.com/questions/2593139/ipad-web-app-detect-virtual-keyboard-using-javascript-in-safari Input focus/blur can indicate, also scrollTop: 
        return {
            left: viewport.left,
            top: viewport.top,
            width: viewport.width,
            height: viewport.height * (viewport.height > viewport.width ? 0.66 : 0.45)  // Fudge factor to allow for keyboard on iPad
        };
    }
    return viewport;
}


function isInput(el) {
    var tagName = el && el.tagName && el.tagName.toLowerCase();
    return (tagName == 'input' && el.type != 'button' && el.type != 'radio' && el.type != 'checkbox') || (tagName == 'textarea');
};

The above code is only approximate: It is wrong for split keyboard, undocked keyboard, physical keyboard. As per comment at top, you may be able to do a better job than the given code on Safari (since iOS8?) or WKWebView (since iOS10) using window.innerHeightproperty.

上面的代码只是大概的:分体键盘、非对接键盘、物理键盘都是错误的。根据顶部的评论,您可以使用window.innerHeight属性在 Safari(从 iOS8 开始?)或 WKWebView(从 iOS10 开始)上比给定的代码做得更好。

I have found failures under other circumstances: e.g. give focus to input then go to home screen then come back to page; iPad shouldnt make viewport smaller; old IE browsers won't work, Opera didnt work because Opera kept focus on element after keyboard closed.

我发现其他情况下的失败:例如,将焦点放在输入然后转到主屏幕然后返回页面;iPad 不应该让视口变小;旧的 IE 浏览器将无法工作,Opera 无法工作,因为 Opera 在键盘关闭后仍然专注于元素。

However the tagged answer (changing scrolltop to measure height) has nasty UI side effects if viewport zoomable (or force-zoom enabled in preferences). I don't use the other suggested solution (changing scrolltop) because on iOS, when viewport is zoomable and scrolling to focused input, there are buggy interactions between scrolling & zoom & focus (that can leave a just focused input outside of viewport - not visible).

但是,如果视口可缩放(或在首选项中启用强制缩放),标记的答案(更改滚动顶部以测量高度)会产生令人讨厌的 UI 副作用。我不使用其他建议的解决方案(更改滚动顶部),因为在 iOS 上,当视口可缩放并滚动到焦点输入时,滚动和缩放和焦点之间存在错误的交互(这可能会在视口之外留下一个刚刚聚焦的输入 - 不是可见的)。

回答by user1650613

Only tested on Android 4.1.1:

仅在 Android 4.1.1 上测试:

blur event is not a reliable event to test keyboard up and down because the user as the option to explicitly hide the keyboard which does not trigger a blur event on the field that caused the keyboard to show.

模糊事件不是一个可靠的向上和向下测试键盘的事件,因为用户可以选择显式隐藏键盘,这不会在导致键盘显示的字段上触发模糊事件。

resize event however works like a charm if the keyboard comes up or down for any reason.

但是,如果键盘因任何原因向上或向下移动,则调整大小事件就像一个魅力。

coffee:

咖啡:

$(window).bind "resize", (event) ->  alert "resize"

fires on anytime the keyboard is shown or hidden for any reason.

键盘因任何原因显示或隐藏时触发。

Note however on in the case of an android browser (rather than app) there is a retractable url bar which does not fire resize when it is retracted yet does change the available window size.

但是请注意,在 android 浏览器(而不是应用程序)的情况下,有一个可伸缩的 url 栏,它在收回时不会触发调整大小,但会更改可用的窗口大小。

回答by K.A

Instead of detecting the keyboard, try to detect the size of the window

尝试检测窗口大小,而不是检测键盘

If the height of the window was reduced, and the width is still the same, it means that the keyboard is on. Else the keyboard is off, you can also add to that, test if any input field is on focus or not.

如果窗口的高度减小了,而宽度仍然不变,则表示键盘已打开。否则键盘关闭,您也可以添加,测试是否有任何输入字段处于焦点状态。

Try this code for example.

例如,试试这个代码。

var last_h = $(window).height(); //  store the intial height.
var last_w = $(window).width(); //  store the intial width.
var keyboard_is_on = false;
$(window).resize(function () {
    if ($("input").is(":focus")) {
        keyboard_is_on =
               ((last_w == $(window).width()) && (last_h > $(window).height()));
    }   
});     

回答by Flow

As noted in the previous answers somewhere the window.innerHeight variable gets updated properly now on iOS10when the keyboard appears and since I don't need the support for earlier versions I came up with the following hack that might be a bit easier then the discussed "solutions".

正如在前面的答案中指出的那样,当键盘出现时,window.innerHeight 变量现在在 iOS10 上正确更新,并且由于我不需要对早期版本的支持,我想出了以下可能比讨论的更容易一些的技巧“解决方案”。

//keep track of the "expected" height
var windowExpectedSize = window.innerHeight;

//update expected height on orientation change
window.addEventListener('orientationchange', function(){
    //in case the virtual keyboard is open we close it first by removing focus from the input elements to get the proper "expected" size
    if (window.innerHeight != windowExpectedSize){
        $("input").blur();
        $("div[contentEditable]").blur();     //you might need to add more editables here or you can focus something else and blur it to be sure
        setTimeout(function(){
            windowExpectedSize = window.innerHeight;
        },100);
    }else{
        windowExpectedSize = window.innerHeight;
    }
});

//and update the "expected" height on screen resize - funny thing is that this is still not triggered on iOS when the keyboard appears
window.addEventListener('resize', function(){
    $("input").blur();  //as before you can add more blurs here or focus-blur something
    windowExpectedSize = window.innerHeight;
});

then you can use:

那么你可以使用:

if (window.innerHeight != windowExpectedSize){ ... }

to check if the keyboard is visible. I've been using it for a while now in my web app and it works well, but (as all of the other solutions) you might find a situation where it fails because the "expected" size is not updated properly or something.

检查键盘是否可见。我已经在我的网络应用程序中使用它一段时间了,它运行良好,但是(与所有其他解决方案一样)您可能会发现它失败的情况,因为“预期”大小未正确更新或其他原因。

回答by Nalini Amir

Try this one:

试试这个:

var lastfoucsin;

$('.txtclassname').click(function(e)
{
  lastfoucsin=$(this);

//the virtual keyboard appears automatically

//Do your stuff;

});


//to check ipad virtual keyboard appearance. 
//First check last focus class and close the virtual keyboard.In second click it closes the wrapper & lable

$(".wrapperclass").click(function(e)
{

if(lastfoucsin.hasClass('txtclassname'))
{

lastfoucsin=$(this);//to avoid error

return;

}

//Do your stuff 
$(this).css('display','none');
});`enter code here`