it-swarm-pt.tech

iPad Web App: Detectar teclado virtual usando JavaScript no Safari?

Estou escrevendo um aplicativo da web para o iPad ( não é um aplicativo regular da App Store - escrito usando HTML, CSS e JavaScript). Como o teclado preenche uma grande parte da tela, faria sentido alterar o layout do aplicativo para se ajustar ao espaço restante quando o teclado for exibido. No entanto, não encontrei nenhuma maneira de detectar quando ou se o teclado é exibido.

Minha primeira ideia foi assumir que o teclado é visível quando um campo de texto tem foco. No entanto, quando um teclado externo é anexado a um iPad, o teclado virtual não aparece quando um campo de texto recebe foco.

Em meus experimentos, o teclado também não afetou a altura ou a altura da rolagem de nenhum dos elementos DOM, e não encontrei nenhum evento ou propriedade proprietária que indique se o teclado está visível.

137
LKM

Eu encontrei uma solução que funciona, embora seja um pouco feia. Também não funciona em todas as situações, mas funciona para mim. Desde que eu estou adaptando o tamanho da interface do usuário para o tamanho da janela do iPad, o usuário normalmente não consegue rolar. Em outras palavras, se eu definir o scrollTop da janela, ele permanecerá em 0.

Se, por outro lado, o teclado é mostrado, a rolagem de repente funciona. Assim, posso definir scrollTop, testar imediatamente seu valor e redefini-lo. Veja como isso pode parecer no código, usando 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 ');
    });
});

Normalmente, você esperaria que isso não fosse visível para o usuário. Infelizmente, pelo menos quando rodando no Simulador, o iPad visivelmente (embora rapidamente) rola para cima e para baixo novamente. Ainda assim, funciona, pelo menos em algumas situações específicas.

Eu testei isso em um iPad e parece funcionar bem.

53
LKM

Você pode usar o evento focusout para detectar a exclusão do teclado. É como borrão, mas bolhas. Ele irá disparar quando o teclado for fechado (mas também em outros casos, é claro). No Safari e no Chrome, o evento só pode ser registrado com addEventListener, não com métodos legados. Aqui está um exemplo que usei para restaurar um aplicativo Phonegap após a exclusão do teclado. 

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

Sem esse snippet, o contêiner do aplicativo permaneceu na posição de rolagem para cima até a atualização da página.

28
Per Quested Aronsson

talvez uma solução um pouco melhor seja ligar (com jQuery no meu caso) o evento "blur" nos vários campos de entrada.

Isso porque quando o teclado desaparece todos os campos do formulário são borrados. Então, para a minha situação, este recorte resolveu o problema.

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

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

});

espero que ajude. Michele

15
Michele

Se houver um teclado na tela, a focalização de um campo de texto próximo à parte inferior da viewport fará com que o Safari role o campo de texto para a visualização. Pode haver alguma maneira de explorar esse fenômeno para detectar a presença do teclado (com um pequeno campo de texto na parte inferior da página que ganha foco momentaneamente, ou algo parecido).

14
ianh

Durante o evento de foco, você pode rolar além da altura do documento e, magicamente, o window.innerHeight é reduzido pela altura do teclado virtual. Observe que o tamanho do teclado virtual é diferente para as orientações paisagem x retrato, portanto, será necessário redetectá-lo quando ele for alterado. Aconselho que não se lembre desses valores, pois o usuário pode conectar/desconectar um teclado Bluetooth a qualquer momento.

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

Observe que quando o usuário está usando um teclado bluetooth, o keyboardHeight é 44, que é a altura da barra de ferramentas [anterior] [próximo].

Há um pouco de cintilação quando você faz essa detecção, mas não parece possível evitá-lo.

11
Hafthor

Edit: Documentado pela Apple, embora eu não conseguisse fazê-lo funcionar: WKWebView Behavior with Keyboard Displays : "No iOS 10, os objetos WKWebView correspondem ao comportamento nativo do Safari, atualizando sua propriedade window.innerHeight quando o teclado é exibido, e não chame eventos de redimensionamento "(talvez seja possível usar foco ou foco mais atraso para detectar o teclado em vez de usar o redimensionamento).

Edit: código presume teclado na tela, teclado não externo. Deixá-lo porque as informações podem ser úteis para outras pessoas que só se preocupam com os teclados na tela. Use http://jsbin.com/AbimiQup/4 para visualizar os parâmetros da página.

Nós testamos para ver se document.activeElement é um elemento que mostra o teclado (input type = text, textarea, etc).

O código a seguir falsifica as coisas para nossos propósitos (embora geralmente não esteja correto).

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

O código acima é apenas aproximado: é errado para o teclado dividido, o teclado desancorado, o teclado físico. Como comentário no topo, você pode ser capaz de fazer um trabalho melhor do que o código dado no Safari (desde iOS8?) Ou WKWebView (desde iOS10) usando a propriedade window.innerHeight

Encontrei falhas em outras circunstâncias: por ex. dê foco à entrada e vá para a tela inicial e volte para a página; O iPad não deve tornar a viewport menor; navegadores antigos IE não funcionam, o Opera não funciona porque o Opera mantém o foco no elemento depois que o teclado é fechado.

No entanto, a resposta marcada (mudança de rolagem para medir a altura) tem desagradáveis ​​efeitos colaterais da interface do usuário se a janela de exibição for zoom (ou forçar o zoom nas preferências). Eu não uso a outra solução sugerida (mudança de scrolltop) porque no iOS, quando a viewport está com zoom e rolando para a entrada focada, existem interações com bugs entre scrolling & zoom & focus (que podem deixar uma entrada focada fora da viewport - não visível).

8
robocat

Apenas testado no Android 4.1.1:

o evento de desfocagem não é um evento fiável para testar o teclado para cima e para baixo porque o utilizador tem a opção de ocultar explicitamente o teclado, o que não desencadeia um evento de desfocagem no campo que causou a apresentação do teclado.

no entanto, o evento de redimensionamento funciona como um encanto se o teclado subir ou descer por qualquer motivo.

café: 

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

dispara sempre que o teclado é mostrado ou oculto por qualquer motivo.

Note, no entanto, no caso de um navegador Android (em vez de aplicativo), há uma barra de URL retrátil que não dispara redimensionar quando é recolhida, mas altera o tamanho da janela disponível.

5
user1650613

Em vez de detectar o teclado, tente detectar o tamanho da janela

Se a altura da janela foi reduzida e a largura ainda é a mesma, significa que o teclado está ligado. Se o teclado estiver desligado, você também pode adicionar, testar se algum campo de entrada está ativado foco ou não.

Tente este código por exemplo.

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()));
    }   
});     
3
K.A

Como observado nas respostas anteriores, em algum lugar a variável window.innerHeight é atualizada corretamente agora no iOS10 quando o teclado aparece e já que eu não preciso do suporte para versões anteriores eu criei o seguinte hack que pode ser um pouco mais fácil então as "soluções" discutidas.

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

então você pode usar:

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

para verificar se o teclado está visível. Eu tenho usado por um tempo agora no meu aplicativo da web e funciona bem, mas (como todas as outras soluções) você pode encontrar uma situação em que ele falha porque o tamanho "esperado" não é atualizado corretamente ou algo assim.

1
Flow

Tente este:

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`
1
Nalini Amir

Esta solução lembra a posição de rolagem 

    var currentscroll = 0;

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

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

        $(window).scrollTop(currentscroll);

        }
    });
1
WebsterDevelopine

No navegador Safari, quando um elemento é posicionado na parte inferior da tela e o teclado virtual é visível, essa posição aumenta em cerca de 1px. Você pode explorar isso.

Suponha que é um dispositivo móvel no modo paisagem

<div id="detector" style="position: absolute; bottom: 0"></div>

const detector = document.querySelector('#detector');
detector.getBoundingClientRect().bottom // 320

// when virtual keyboard is visible
detector.getBoundingClientRect().bottom // 329 or 328
0
Udo

Talvez seja mais fácil ter uma caixa de seleção nas configurações do seu aplicativo, onde o usuário pode alternar entre "teclado externo conectado".

Em letras pequenas, explique ao usuário que atualmente os teclados externos não são detectáveis ​​nos navegadores atuais.

0
Ian White

Eu não tentei isso sozinho, por isso é apenas uma idéia ... mas você já tentou usar consultas de mídia com CSS para ver quando a altura da janela muda e, em seguida, alterar o design para isso? Eu imagino que o Safari Mobile não está reconhecendo o teclado como parte da janela, então espero que funcione.

Exemplo:

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

O problema é que, mesmo em 2014, os dispositivos gerenciam os eventos de redimensionamento da tela, bem como os eventos de rolagem, de maneira inconsistente, enquanto o teclado virtual está aberto.

Descobri que, mesmo se você estiver usando um teclado bluetooth, o iOS em particular aciona alguns bugs de layout estranhos; Então, em vez de detectar um teclado virtual, acabei de segmentar dispositivos que são muito estreitos e têm telas sensíveis ao toque.

Eu uso media queries (ou window.matchMedia ) para detecção de largura e Modernizr para detecção de eventos de toque.

0
pixelbandito

Eu fiz algumas pesquisas, e não consegui encontrar nada concreto para um "no teclado mostrado" ou "no teclado dispensado". Veja a lista oficial de eventos suportados . Veja também Nota Técnica TN2262 para iPad. Como você provavelmente já sabe, há um evento corporal onorientationchange que você pode conectar para detectar paisagem/retrato.

Da mesma forma, mas um palpite ... você já tentou detectar redimensionar? Alterações na janela de exibição podem acionar o evento indiretamente a partir do teclado que está sendo mostrado/oculto.

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

O que simplesmente alertaria a nova altura em qualquer evento de redimensionamento ....

0
slf