Aplicación web iPad: ¿Detectar teclado virtual usando JavaScript en Safari?

147

Estoy escribiendo una aplicación web para iPad ( no una aplicación normal de la App Store , está escrita con HTML, CSS y JavaScript). Dado que el teclado llena una gran parte de la pantalla, tendría sentido cambiar el diseño de la aplicación para que se ajuste al espacio restante cuando se muestra el teclado. Sin embargo, no he encontrado ninguna manera de detectar cuándo o si se muestra el teclado.

Mi primera idea fue suponer que el teclado es visible cuando un campo de texto tiene el foco. Sin embargo, cuando se conecta un teclado externo a un iPad, el teclado virtual no aparece cuando un campo de texto recibe el foco.

En mis experimentos, el teclado tampoco afectó la altura o la altura de desplazamiento de ninguno de los elementos DOM, y no he encontrado eventos o propiedades de propiedad que indiquen si el teclado es visible.

LKM
fuente
1
Hm, problema interesante. Intenta iterar sobre los objetos de "ventana" en el Safari de iPad para ver si hay algún objeto especial relacionado con el soporte del teclado.
David Murdoch
@David que no funcionará, el teclado no es una "ventana" de Javascript.
kennytm
2
@KennyTM. Duh Pero puede haber una bandera relacionada con la visualización del teclado en pantalla en cualquiera de los objetos de la ventana. Vale la pena intentarlo.
David Murdoch
1
Lo intenté No encontré nada, desafortunadamente. También comparó todas las propiedades de la ventana con tres niveles de profundidad antes y después de mostrar el teclado. Ninguna de las diferencias parecía relevante como indicador para el teclado.
LKM
3
¿Hay una nueva respuesta para esto?
fraxture

Respuestas:

54

Encontré una solución que funciona, aunque es un poco fea. Tampoco funcionará en todas las situaciones, pero funciona para mí. Como estoy adaptando el tamaño de la interfaz de usuario al tamaño de la ventana del iPad, el usuario normalmente no puede desplazarse. En otras palabras, si configuro el scrollTop de la ventana, permanecerá en 0.

Si, por otro lado, se muestra el teclado, el desplazamiento funciona de repente. Entonces puedo configurar scrollTop, probar inmediatamente su valor y luego restablecerlo. Así es como podría verse eso en el 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, esperaría que esto no sea visible para el usuario. Desafortunadamente, al menos cuando se ejecuta en el simulador, el iPad se desplaza visiblemente (aunque rápidamente) hacia arriba y hacia abajo nuevamente. Aún así, funciona, al menos en algunas situaciones específicas.

He probado esto en un iPad, y parece funcionar bien.

LKM
fuente
Tengo un problema con mi aplicación web en la que cuando la entrada se enfoca, la pantalla se desplaza un poco hacia arriba. De lo contrario, he desactivado el desplazamiento, pero aún así se desplaza. ¿Algunas ideas? Gracias [ stackoverflow.com/questions/6740253/…
Andrew Samuelsen
Todavía no lo he probado, pero parece prometedor. ¿No .scrollTop(1)funcionaría igual de bien y sería menos obvio?
ThinkingStiff
1
Esta es una mala idea ... El teclado podría ser bluetooth y virtual podría no mostrarse.
theSociableme
3
@theSociableme: El objetivo de esta solución es manejar el teclado bluetooth correctamente. Si ignoraste los teclados bluetooth, averiguar si se mostraba el teclado virtual sería fácil, ya que podrías verificar si un campo tenía foco.
LKM
55
@fraxture: No conozco una explicación completa (si investigas y escribes una, me encantaría leerla). Las dos plataformas manejan los teclados en pantalla en sus principales navegadores de manera muy diferente. Android Chrome reduce la altura de la ventana gráfica para dejar espacio para el teclado, por lo que la página cambia de tamaño cuando se muestra el teclado. iOS Safari superpone la página con el teclado (el tamaño de la página sigue siendo el mismo) y cambia el funcionamiento del desplazamiento. Safari desplaza la página dentro de la ventana gráfica y simultáneamente la mueve, asegurando que la parte inferior de la página esté sobre el teclado cuando se desplaza hacia abajo.
LKM
32

Puede usar el evento de enfoque para detectar el descarte del teclado. Es como desenfoque, pero burbujas. Se disparará cuando se cierre el teclado (pero también en otros casos, por supuesto). En Safari y Chrome, el evento solo se puede registrar con addEventListener, no con métodos heredados. Aquí hay un ejemplo que usé para restaurar una aplicación Phonegap después de descartar el teclado.

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

Sin este fragmento, el contenedor de la aplicación permaneció en la posición de desplazamiento hacia arriba hasta que se actualiza la página.

Por Quested Aronsson
fuente
1
mejor solución que encontré para mi problema
Sutulustus
1
También puede usar la versión 'focusin' para detectar el teclado abierto.
A.Çetin
15

quizás una solución un poco mejor es vincular (con jQuery en mi caso) el evento "desenfoque" en los diversos campos de entrada.

Esto se debe a que cuando el teclado desaparece, todos los campos del formulario se ven borrosos. Entonces, para mi situación, este recorte resolvió el problema.

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

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

});

Espero eso ayude. Michele

Michele
fuente
Gracias por esta respuesta Me pareció útil resolver un problema en el que el teclado Safari de iPad causaba que el cursor del área de texto fuera desplazado (desplazado) fuera del área de texto.
kennbrodhagen
14

Si hay un teclado en pantalla, enfocar un campo de texto que esté cerca de la parte inferior de la ventana gráfica hará que Safari desplace el campo de texto a la vista. Puede haber alguna forma de explotar este fenómeno para detectar la presencia del teclado (que tiene un pequeño campo de texto en la parte inferior de la página que se enfoca momentáneamente, o algo así).

ianh
fuente
1
Esa es una idea ingeniosa. Encontré una solución similar que también usa la posición de desplazamiento actual para detectar el teclado virtual.
LKM
brillante! me salvaste el día!
aztack el
11

Durante el evento de enfoque, puede desplazarse más allá de la altura del documento y mágicamente la ventana. La altura interna se reduce por la altura del teclado virtual. Tenga en cuenta que el tamaño del teclado virtual es diferente para las orientaciones horizontal y vertical, por lo que deberá volver a detectarlo cuando cambie. Aconsejaría no recordar estos valores, ya que el usuario podría conectar / desconectar un teclado Bluetooth en cualquier 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;
};

Tenga en cuenta que cuando el usuario usa un teclado bluetooth, la altura del teclado es 44, que es la altura de la barra de herramientas [anterior] [siguiente].

Hay un pequeño parpadeo cuando haces esta detección, pero no parece posible evitarlo.

Hafthor
fuente
55
Acabo de probar esto en iOS 8.2 y no funciona ... ¿dejó de funcionar en algún momento para un nuevo iOS?
Ming
1
Tampoco me ha funcionado: el cambio de tamaño no se activa en iOS9.3
llamerr
8

Editar: Documentado por Apple, aunque en realidad no pude hacerlo funcionar: Comportamiento WKWebView con pantallas de teclado : "En iOS 10, los objetos WKWebView coinciden con el comportamiento nativo de Safari al actualizar su propiedad window.innerHeight cuando se muestra el teclado, y no invocan cambiar el tamaño de los eventos "(quizás puede usar el enfoque o el enfoque más el retraso para detectar el teclado en lugar de usar el cambio de tamaño).

Editar: el código presupone el teclado en pantalla, no el teclado externo. Dejarlo porque la información puede ser útil para otros que solo se preocupan por los teclados en pantalla. Use http://jsbin.com/AbimiQup/4 para ver los parámetros de la página.

Probamos para ver si document.activeElementes un elemento que muestra el teclado (tipo de entrada = texto, área de texto, etc.).

El siguiente código falsifica las cosas para nuestros propósitos (aunque generalmente no es correcto).

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

El código anterior es solo aproximado: es incorrecto para teclado dividido, teclado desacoplado, teclado físico. Según el comentario en la parte superior, es posible que pueda hacer un mejor trabajo que el código dado en Safari (¿desde iOS8?) O WKWebView (desde iOS10) usando la window.innerHeightpropiedad.

He encontrado fallas en otras circunstancias: por ejemplo, enfocar la entrada, luego ir a la pantalla de inicio y luego volver a la página; iPad no debería hacer que la ventana gráfica sea más pequeña; los antiguos navegadores IE no funcionarán, Opera no funcionó porque Opera mantuvo el foco en el elemento después de cerrar el teclado.

Sin embargo, la respuesta etiquetada (cambiar la tapa de desplazamiento para medir la altura) tiene efectos secundarios desagradables en la interfaz de usuario si la ventana se puede ampliar (o el zoom forzado está habilitado en las preferencias). No utilizo la otra solución sugerida (cambiar el scrolltop) porque en iOS, cuando la ventana gráfica se puede ampliar y desplazarse a la entrada enfocada, hay interacciones con errores entre desplazamiento y zoom y enfoque (que pueden dejar una entrada enfocada solo fuera de la ventana gráfica, no visible).

robocat
fuente
Dependiendo de los navegadores innerHeight para detectar roturas de pantalla completa cuando algunos elementos se posicionan absolutamente. No es fiable en absoluto.
Udo
5

Solo probado en Android 4.1.1:

El evento de desenfoque no es un evento confiable para probar el teclado hacia arriba y hacia abajo porque el usuario tiene la opción de ocultar explícitamente el teclado que no desencadena un evento de desenfoque en el campo que hizo que se mostrara el teclado.

Sin embargo, cambiar el tamaño del evento funciona de maravilla si el teclado sube o baja por algún motivo.

café:

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

se activa cada vez que se muestra u oculta el teclado por cualquier motivo.

Sin embargo, tenga en cuenta que, en el caso de un navegador de Android (en lugar de una aplicación), hay una barra de URL retráctil que no activa el cambio de tamaño cuando se retrae, pero sí cambia el tamaño de la ventana disponible.

usuario1650613
fuente
+1 para que el evento de desenfoque no se active al descartar manualmente el teclado. Cambiar el tamaño es una buena idea y funcionaría bien para dispositivos Android.
Ankit Garg
Puede confirmar que esto funciona tanto en un iPhone 5 (iOS 6.0.2) como en un iPad 3 (iOS 6.0).
Diego Agulló
1
Acabo de probar en Chrome 41 en iOS6 en CrossBrowserTesting: el cambio de tamaño no se activa cuando el teclado virtual aparece o desaparece.
Dan Dascalescu
4

En lugar de detectar el teclado, intente detectar el tamaño de la ventana

Si la altura de la ventana se redujo y el ancho sigue siendo el mismo, significa que el teclado está encendido. De lo contrario, el teclado está apagado, también puede agregar a eso, probar si algún campo de entrada está enfocado o no.

Prueba este código por ejemplo.

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()));
    }   
});     
KA
fuente
2
Parece que ya no funciona en iOS 8. El teclado superpone el contenido y, en muchos casos, el contenido se desplaza hacia abajo oscureciendo los campos de entrada inicialmente enfocados.
Rick Strahl
3
La altura de la ventana devuelve la altura, incluido el teclado desde iOS 7, en la ventana IOS6. La altura cambia cuando se abre el teclado.
Michiel
1
Tenga en cuenta que la altura también cambia cuando la barra de direcciones superior se desliza dentro y fuera de la pantalla al desplazarse. Debería agregar un cambio mínimo de altura de, diría, 200 px (no probado).
oriadam
1

Esta solución recuerda la posición de desplazamiento.

    var currentscroll = 0;

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

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

        $(window).scrollTop(currentscroll);

        }
    });
WebsterDevelopine
fuente
1

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

Como se señaló en las respuestas anteriores en alguna parte, la variable window.innerHeight se actualiza correctamente ahora en iOS10 cuando aparece el teclado y, dado que no necesito el soporte para versiones anteriores, se me ocurrió el siguiente truco que podría ser un poco más fácil que el discutido "soluciones".

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

entonces puedes usar:

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

para verificar si el teclado está visible. Lo he estado usando durante un tiempo en mi aplicación web y funciona bien, pero (como todas las otras soluciones) puede encontrar una situación en la que falla porque el tamaño "esperado" no se actualiza correctamente o algo así.

Fluir
fuente
Esperaba que este fuera el caso, pero no, no se actualiza.
Sam Saffron
0

Hice un poco de búsqueda, y no pude encontrar nada concreto para un "en el teclado mostrado" o "en el teclado descartado". Consulte la lista oficial de eventos compatibles . Consulte también la Nota técnica TN2262 para iPad. Como probablemente ya sepa, hay un evento corporal onorientationchangeque puede conectar para detectar el paisaje / retrato.

Del mismo modo, pero una suposición salvaje ... ¿has intentado detectar el cambio de tamaño? Los cambios en la ventana gráfica pueden activar ese evento indirectamente desde el teclado que se muestra / oculta.

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

Lo que simplemente alertaría a la nueva altura en cualquier evento de cambio de tamaño ...

slf
fuente
10
Desafortunadamente, en mis pruebas, el teclado no activó el evento de cambio de tamaño.
LKM
0

No lo he intentado yo mismo, así que es solo una idea ... pero ¿has intentado usar consultas de medios con CSS para ver cuándo cambia la altura de la ventana y luego cambiar el diseño para eso? Me imagino que Safari mobile no reconoce el teclado como parte de la ventana, por lo que con suerte funcionaría.

Ejemplo:

@media all and (height: 200px){
    #content {height: 100px; overflow: hidden;}
}
Janae
fuente
2
Muy buena idea. Desafortunadamente, en mis pruebas, mostrar el teclado no afectó los valores de altura utilizados para evaluar las consultas de medios.
LKM
Puedo confirmar: height: 250px funcionó para mí (en Android, al menos).
WoodrowShigeru
0

El problema es que, incluso en 2014, los dispositivos manejan eventos de cambio de tamaño de pantalla, así como eventos de desplazamiento, inconsistentemente mientras el teclado virtual está abierto.

Descubrí que, incluso si está utilizando un teclado bluetooth, iOS en particular desencadena algunos errores de diseño extraños; así que en lugar de detectar un teclado virtual, solo tuve que apuntar a dispositivos que son muy estrechos y tienen pantallas táctiles.

Utilizo consultas multimedia (o window.matchMedia ) para la detección de ancho y Modernizr para la detección de eventos táctiles.

pixelbandito
fuente
0

Quizás sea más fácil tener una casilla de verificación en la configuración de su aplicación donde el usuario puede alternar 'teclado externo conectado'.

En letra pequeña, explique al usuario que los teclados externos actualmente no son detectables en los navegadores actuales.

Ian White
fuente
1
Agregar un conmutador como este es un último recurso que no debe considerarse aceptable en absoluto a menos que no haya otra solución que no rompa la aplicación. Esto no es algo que deba bloquear la producción de una aplicación que funcione.
Adam Leggett
-2

Bueno, puedes detectar cuándo tus cuadros de entrada tienen el foco, y sabes la altura del teclado. También hay CSS disponible para obtener la orientación de la pantalla, así que creo que puedes hackearlo.

Sin embargo, de alguna manera querrás manejar la carcasa de un teclado físico.

acrobacias
fuente