ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

嗯,有趣的问题。尝试在 iPad 的 Safari 上迭代“window”的对象,看看是否有任何与键盘支持相关的特殊对象。
@David 那行不通,键盘不是Javascript“窗口”。
@肯尼TM。呃。但是在任何窗口的对象中都可能有一个与屏幕键盘显示相关的标志。值得一试。
我试过了。没有找到任何东西,很遗憾。还比较了显示键盘前后三层深度的所有窗口属性。这些差异似乎都与键盘的指标无关。
这个有更新的答案吗??

L
LKM

我找到了一个可行的解决方案,虽然它有点难看。它也不会在所有情况下都有效,但它对我有用。由于我正在调整用户界面的大小以适应 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 上进行了测试,它似乎工作正常。


我的网络应用程序出现问题,当输入焦点集中在屏幕上时,屏幕会向上滚动一点。我已经禁用了滚动,但仍然可以滚动。有任何想法吗?谢谢 [stackoverflow.com/questions/6740253/…
我还没有尝试过,但它看起来很有希望。 .scrollTop(1) 不会同样有效并且不那么明显吗?
这是个坏主意...键盘可能是蓝牙并且可能无法显示虚拟键盘。
@theSociableme:这个解决方案的重点是正确处理蓝牙键盘。如果您忽略了蓝牙键盘,那么确定是否显示虚拟键盘将很容易,因为您只需检查一个字段是否有焦点。
@fraxture:不知道一个全面的解释(如果你研究并写一个,我很乐意阅读它)。这两个平台在其主要浏览器中处理屏幕键盘的方式非常不同。 Android Chrome 会缩小视口高度以为键盘腾出空间,因此在显示键盘时页面会调整大小。 iOS Safari 用键盘覆盖页面(页面大小保持不变),并改变滚动的工作方式。 Safari 在视口内滚动页面,同时移动视口,确保在一直向下滚动时页面底部位于键盘上方。
P
Per Quested Aronsson

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

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

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


我为我的问题找到的最佳解决方案
您也可以使用“focusin”版本来检测键盘是否打开。
不,不幸的是,这并不能解决最初的问题,因为 focusout 是在由于任何原因失去焦点时触发的。因此,它无助于确定虚拟键盘是否曾经打开过,或者是否使用了外部键盘并且该字段具有焦点而没有使用虚拟键盘。
i
ianh

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


这是一个巧妙的想法。我找到了一个类似的解决方案,它也使用当前滚动位置来检测虚拟键盘。
太棒了!你拯救了我的一天!
M
Michele

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

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

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

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

});

希望能帮助到你。米歇尔


感谢您的回答。我发现它有助于解决 iPad Safari 键盘导致 textarea 光标在 textarea 之外移位(偏移)的问题。
r
robocat

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

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

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

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

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

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

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

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


当某些元素被绝对定位时,根据浏览器的 innerHeight 来检测全屏中断。一点都不可靠。
H
Hafthor

在焦点事件期间,您可以滚动超过文档高度,并且 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,即 [previous][next] 工具栏的高度。

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


我刚刚在 iOS 8.2 中尝试过,但它不起作用......它是否在新 iOS 的某个阶段停止工作?
也没有为我工作 - 在 iOS9.3 中不会触发调整大小
virtualKeyboardHeight 函数帮助我避免滚动离开移动设备上全屏输入的搜索字段。在 iOS 上,当输入字段位于屏幕下方 60% 以内时,它总是被键盘推出屏幕。我尝试过的其他滚动功能根本没有帮助。
u
user1650613

仅在 Android 4.1.1 上测试:

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

但是,如果键盘因任何原因向上或向下,resize 事件就像一个魅力。

咖啡:

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

无论出于何种原因显示或隐藏键盘时都会触发。

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


+1 用于手动关闭键盘时未触发的模糊事件。调整大小是个好主意,它适用于 Android 设备。
可以确认这适用于 iPhone 5 (iOS 6.0.2) 和 iPad 3 (iOS 6.0)。
刚刚在 CrossBrowserTesting 上的 iOS6 上的 Chrome 41 上进行了测试 - 调整大小不会由虚拟键盘的出现或消失触发。
V
Vamsi

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

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

例如,试试这个代码。

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

这在 iOS 8 中似乎不再起作用了。键盘覆盖了内容,并且在许多情况下,内容向下滚动会遮盖最初聚焦的输入字段。
从 iOS 7 开始,窗口高度返回包括键盘在内的高度,在 IOS6 中,window.height 会在键盘打开时发生变化。
请注意,当顶部地址栏在滚动时滑入和滑出屏幕时,高度也会发生变化。你应该添加一个最小高度变化,我会说,200px(未经测试)。
d
david_adler

visual viewport API 用于对虚拟键盘更改和视口可见性做出反应。

Visual Viewport API 提供了一种明确的机制来查询和修改窗口的可视视口的属性。视觉视口是屏幕的视觉部分,不包括屏幕键盘、捏拉缩放区域之外的区域或任何其他不随页面尺寸缩放的屏幕工件。

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

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

这是当今最好的两种方法之一。 window.visualViewport(如果存在)在 Android 和 iOS 上的浏览器 chrome 隐藏/显示以及屏幕键盘显示/隐藏时触发 resize 事件。它也会在其他事件发生时触发,例如浏览器调整大小和捏缩放。另一个不错的选择是 web.dev/virtualkeyboard,但它仅适用于 Android/Chrome。
K
Kao

试试这个:

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`

R
Ruce Bee

这个想法是将固定的 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('键盘高度', window.innerHeight - keyboardAnchor.getBoundingClientRect().bottom) }, true)


W
WebsterDevelopine

此解决方案记住滚动位置

    var currentscroll = 0;

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

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

        $(window).scrollTop(currentscroll);

        }
    });

L
L84

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

我发现,即使你使用的是蓝牙键盘,iOS 也会特别触发一些奇怪的布局错误;因此,我不必检测软键盘,而只需要针对非常窄且具有触摸屏的设备。

我使用媒体查询(或 window.matchMedia)进行宽度检测,使用 Modernizr 进行触摸事件检测。


F
Flow

如前一个答案中所述,当键盘出现时,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;
});

那么你可以使用:

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

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


我希望是这样,但不,它没有得到更新。
不幸的是,在 iOS 14 中,window.innerHeight 似乎不受虚拟键盘状态的影响。
I
Ian White

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

用小字体向用户说明当前浏览器无法检测到外部键盘。


添加这样的切换是最后的手段,除非没有其他解决方案不会破坏应用程序,否则根本不应该被认为是可以接受的。这不应该成为生产功能应用程序的障碍。
s
slf

我进行了一些搜索,但找不到“在键盘上显示”或“在键盘上被解雇”的任何具体内容。请参阅the official list of supported events。另请参阅 iPad 的 Technical Note TN2262。您可能已经知道,您可以连接一个身体事件 onorientationchange 来检测横向/纵向。

同样,但一个疯狂的猜测......你有没有尝试检测调整大小?视口更改可能会从显示/隐藏的键盘间接触发该事件。

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

这只会在任何调整大小事件时提醒新高度......


不幸的是,在我的测试中,键盘没有触发调整大小事件。
j
jww

我自己没有尝试过,所以这只是一个想法……但是您是否尝试过使用带有 CSS 的媒体查询来查看窗口高度何时发生变化,然后为此更改设计?我想 Safari 移动版没有将键盘识别为窗口的一部分,因此希望可以正常工作。

例子:

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

很聪明的主意。不幸的是,在我的测试中,显示键盘并不会影响用于评估媒体查询的高度值。
我可以确认: height: 250px 对我有用(至少在 Android 上)。
s
stuntmouse

好吧,您可以检测输入框何时获得焦点,并且您知道键盘的高度。还有可用的 CSS 来获取屏幕的方向,所以我认为你可以破解它。

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