IPadWebApp: 在 Safari 中使用 JavaScript 检测虚拟键盘?

我正在为 iPad 编写一个网络应用程序(不是普通的应用程序商店应用程序-它使用 HTML、 CSS 和 JavaScript 编写)。由于键盘占据了屏幕的很大一部分,所以当显示键盘时,更改应用程序的布局以适应剩余的空间是有意义的。但是,我没有找到检测键盘何时显示或是否显示的方法。

我的第一个想法是假设当文本字段有焦点时,键盘是可见的。然而,当外部键盘连接到 iPad 上时,当文本字段接收到焦点时,虚拟键盘就不会显示出来。

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

127740 次浏览

我自己还没有尝试过,所以这只是一个想法... 但你有没有尝试使用媒体与 CSS 查询,看看什么时候窗口的高度变化,然后改变该设计?我可以想象,Safari 移动设备不能识别键盘作为窗口的一部分,所以希望能够起作用。

例如:

@media all and (height: 200px){
#content {height: 100px; overflow: hidden;}
}

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

你可以检测到你的输入框什么时候有焦点,你知道键盘的高度。也有 CSS 可用来获得屏幕的方向,所以我认为你可以黑它。

不过,您可能希望以某种方式处理物理键盘的情况。

我做了一些搜索,没有找到任何具体的“键盘显示”或“键盘解散”。见 官方支持的活动清单。也可以参考 技术说明 TN2262 for iPad。正如你可能已经知道,有一个身体事件 onorientationchange你可以连接检测风景/肖像。

类似的,但是大胆猜测一下... 你有没有试过调整尺寸?视图端口更改可能从显示/隐藏的键盘间接触发该事件。

window.addEventListener('resize', function() { alert(window.innerHeight); });

这将简单地提醒新的高度在任何调整大小事件... 。

我找到了一个有效的解决方案,尽管有点难看。它也不会在任何情况下工作,但它为我工作。由于我正在根据 iPad 的窗口大小调整用户界面的大小,用户通常无法滚动屏幕。换句话说,如果我设置窗口的 scrollTop,它将保持为0。

另一方面,如果显示键盘,则滚动突然起作用。因此,我可以设置 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 ');
});
});

通常,您会认为这对用户来说是不可见的。不幸的是,至少在模拟器中运行时,iPad 可以明显地(尽管很快)上下滚动。尽管如此,至少在某些特定情况下,它还是有效的。

我已经在 iPad 上测试过了,效果不错。

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

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

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


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


});

希望能有所帮助。 米歇尔

编辑: 虽然我不能让它真正工作,但是苹果公司提供了文档: WKWebView 键盘显示行为: “在 iOS10中,WKWebView 对象通过在显示键盘时更新他们的 window.innerHeight 属性来匹配 Safari 的本机行为,并且不调用 resize 事件”(也许可以使用聚焦或聚焦加延迟来检测键盘而不是使用 resize)。

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

我们测试 document.activeElement是否是一个显示键盘的元素(输入 type = text,textarea 等)。

下面的代码为我们的目的修改了一些内容(尽管通常不正确)。

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');
};

上面的代码只是近似的: 分割键盘、不对接键盘、物理键盘都是错误的。根据顶部的注释,您可以比 Safari 上的给定代码做得更好(自从 iOS8?)或 WKWebView (自 iOS10)使用 window.innerHeight属性。

我发现在其他情况下也会出现这样的问题: 例如,把注意力放在输入上,然后转到主屏幕,然后再回到页面; iPad 不应该把视窗缩小; 旧的 IE 浏览器不能工作,Opera 不能工作,因为 Opera 在关闭键盘后仍然专注于元素。

然而,如果视口可缩放(或在首选项中启用了强制缩放) ,带标签的答案(将滚动条改为测量高度)会有讨厌的 UI 副作用。我不使用其他建议的解决方案(更改 scrolltop) ,因为在 iOS 上,当 viewport 可以缩放并滚动到有焦点的输入时,在滚动、缩放和焦点之间存在错误的交互(这可能会使一个只有焦点的输入留在 viewport 之外——不可见)。

只在安卓4.1.1上测试过:

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

然而,如果键盘因为任何原因上升或下降,则调整事件的大小可以像魅力一样工作。

咖啡:

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

任何时候显示或隐藏键盘都会触发。

但是注意,在 Android 浏览器(而不是应用程序)的情况下,有一个可伸缩的网址栏,它不会触发调整大小,当它被收回,但不改变可用的窗口大小。

此解决方案记住滚动位置

    var currentscroll = 0;


$('input').bind('focus',function() {
currentscroll = $(window).scrollTop();
});


$('input').bind('blur',function() {
if(currentscroll != $(window).scrollTop()){


$(window).scrollTop(currentscroll);


}
});

在焦点事件期间,您可以滚动过去的文档高度和神奇的 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;
};

请注意,当用户使用蓝牙键盘时,keyboardHeight 是44,它是[上一个][下一个]工具栏的高度。

当你做这个检测的时候会有一点点闪烁,但是看起来是不可能避免的。

您可以使用 集中注意力事件来检测键盘退出。有点模糊,但是有泡泡。它会在键盘关闭时触发(当然,也会在其他情况下触发)。在 Safari 和 Chrome 中,事件只能用 addEventListener 注册,而不能用遗留方法注册。下面是一个例子,我用来恢复 Phonegap 应用程序后,键盘退出。

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

如果没有这个代码片段,应用程序容器将保持向上滚动的位置,直到页面刷新。

问题是,即使在2014年,当软键盘打开时,设备处理屏幕大小调整事件和滚动事件的方式也不一致。

我发现,即使你使用的是蓝牙键盘,iOS 尤其会触发一些奇怪的布局错误; 因此,我不需要检测软键盘,我只需要瞄准那些非常窄并且有触摸屏的设备。

我使用媒体查询(或 窗口,火柴媒体)进行宽度检测,使用 现代化进行触摸事件检测。

试试这个:

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`

不要检测键盘,尝试检测窗口的大小

如果窗口的高度降低,而宽度仍然相同,则表示键盘处于打开状态。 否则,键盘关闭,您也可以添加到该,测试是否有任何输入字段的焦点或没有。

例如,请尝试使用此代码。

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()));
}
});

正如在前面的答案中提到的,当键盘出现时,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;
});

然后你可以使用:

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

检查键盘是否可见。我已经在我的 web 应用程序中使用它一段时间了,它工作得很好,但是(正如所有其他的解决方案一样)你可能会发现一种情况,它失败了,因为“预期”的大小没有得到适当的更新或者什么的。

也许在应用程序的设置中设置一个复选框更容易,用户可以在其中切换“外部键盘连接?”。

用小字体向用户解释,目前在当今的浏览器中是检测不到外部键盘的。

其思想是在底部添加固定的 div。 显示虚拟键盘时/发生隐藏滚动事件。 另外,我们还知道键盘的高度

const keyboardAnchor = document.createElement('div')
keyboardAnchor.style.position = 'fixed'
keyboardAnchor.style.bottom = 0
keyboardAnchor.style.height = '1px'
document.body.append(keyboardAnchor)
        

window.addEventListener('scroll', ev => {
console.log('keyboard height', window.innerHeight - keyboardAnchor.getBoundingClientRect().bottom)
}, true)


        

可视化视口 API用于对虚拟键盘变化和视口可见性做出反应。

VisualViewportAPI 提供了查询和修改窗口可视化视图属性的显式机制。视觉视口是屏幕的视觉部分,不包括屏幕上的键盘、手指缩放区域以外的区域,或任何其他与页面尺寸无关的屏幕上的工件。

function viewportHandler() {
var viewport = event.target;
console.log('viewport.height', viewport.height)
}


window.visualViewport.addEventListener('scroll', viewportHandler);
window.visualViewport.addEventListener('resize', viewportHandler);