Application Web iPad: détecter le clavier virtuel à l'aide de JavaScript dans Safari?

147

J'écris une application Web pour l'iPad ( pas une application App Store classique - elle est écrite en HTML, CSS et JavaScript). Étant donné que le clavier remplit une grande partie de l'écran, il serait logique de modifier la disposition de l'application pour l'adapter à l'espace restant lorsque le clavier est affiché. Cependant, je n'ai trouvé aucun moyen de détecter quand ou si le clavier est affiché.

Ma première idée était de supposer que le clavier est visible lorsqu'un champ de texte a le focus. Cependant, lorsqu'un clavier externe est connecté à un iPad, le clavier virtuel ne s'affiche pas lorsqu'un champ de texte reçoit le focus.

Dans mes expériences, le clavier n'a pas non plus affecté la hauteur ou la hauteur de défilement des éléments DOM, et je n'ai trouvé aucun événement ou propriété propriétaire indiquant si le clavier est visible.

LKM
la source
1
Hm, problème intéressant. Essayez de parcourir les objets de «fenêtre» sur Safari de l'iPad pour voir s'il existe des objets spéciaux liés à la prise en charge du clavier.
David Murdoch
@David ça ne marche pas, le clavier n'est pas une "fenêtre" Javascript.
kennytm
2
@KennyTM. Duh. Mais il peut y avoir un indicateur lié à l'affichage du clavier à l'écran dans l'un des objets de la fenêtre. Ça vaut le coup.
David Murdoch
1
J'ai essayé ça. Je n'ai rien trouvé, malheureusement. Également comparé toutes les propriétés de la fenêtre à trois niveaux de profondeur avant et après l'affichage du clavier. Aucune des différences ne semble pertinente comme indicateur du clavier.
LKM
3
Y a-t-il une nouvelle réponse à cela ??
fraxture

Réponses:

54

J'ai trouvé une solution qui fonctionne, même si elle est un peu moche. Cela ne fonctionnera pas non plus dans toutes les situations, mais cela fonctionne pour moi. Puisque j'adapte la taille de l'interface utilisateur à la taille de la fenêtre de l'iPad, l'utilisateur est normalement incapable de faire défiler. En d'autres termes, si je règle le scrollTop de la fenêtre, il restera à 0.

Si, au contraire, le clavier est affiché, le défilement fonctionne soudainement. Je peux donc définir scrollTop, tester immédiatement sa valeur, puis la réinitialiser. Voici à quoi cela pourrait ressembler dans le code, en utilisant 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 ');
    });
});

Normalement, vous vous attendez à ce que cela ne soit pas visible pour l'utilisateur. Malheureusement, au moins lors de l'exécution dans le simulateur, l'iPad défile visiblement (mais rapidement) de haut en bas. Pourtant, cela fonctionne, au moins dans certaines situations spécifiques.

J'ai testé cela sur un iPad et cela semble fonctionner correctement.

LKM
la source
J'ai un problème avec mon application Web où, lorsque l'entrée est concentrée, l'écran défile un peu. J'ai par ailleurs désactivé le défilement, mais toujours ce défilement. Des idées? Merci [ stackoverflow.com/questions/6740253/…
Andrew Samuelsen
Je n'ai pas encore essayé cela, mais cela semble prometteur. Ne .scrollTop(1)fonctionnerait -il pas aussi bien et serait-il moins évident?
ThinkingStiff
1
C'est une mauvaise idée ... Le clavier peut être bluetooth et virtuel peut ne pas s'afficher.
theSociableme
3
@theSociableme: L'intérêt de cette solution est de gérer correctement le clavier Bluetooth. Si vous ignoriez les claviers Bluetooth, il serait facile de déterminer si le clavier virtuel était affiché, car vous pouviez simplement vérifier si un champ avait le focus.
LKM
5
@fraxture: Je ne connais pas d'explication complète (si vous recherchez et écrivez une, j'aimerais la lire). Les deux plates-formes gèrent les claviers à l'écran dans leurs principaux navigateurs de manière très différente. Android Chrome réduit la hauteur de la fenêtre pour faire de la place pour le clavier, de sorte que la page se redimensionne lorsque le clavier est affiché. iOS Safari superpose la page avec le clavier (la taille de la page reste la même) et modifie le fonctionnement du défilement. Safari fait à la fois défiler la page à l'intérieur de la fenêtre et déplace simultanément la fenêtre, en s'assurant que le bas de la page est au-dessus du clavier lors du défilement vers le bas.
LKM
32

Vous pouvez utiliser l' événement focusout pour détecter le renvoi du clavier. C'est comme du flou, mais des bulles. Il se déclenchera à la fermeture du clavier (mais aussi dans d'autres cas, bien sûr). Dans Safari et Chrome, l'événement ne peut être enregistré qu'avec addEventListener, pas avec les méthodes héritées. Voici un exemple que j'ai utilisé pour restaurer une application Phonegap après le renvoi du clavier.

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

Sans cet extrait de code, le conteneur d'application est resté en position de défilement vers le haut jusqu'à l'actualisation de la page.

Par Aronsson Quested
la source
1
meilleure solution que j'ai trouvée pour mon problème
Sutulustus
1
Vous pouvez également utiliser la version «focusin» pour détecter l'ouverture du clavier.
A.Çetin
15

peut-être qu'une solution légèrement meilleure est de lier (avec jQuery dans mon cas) l'événement "blur" sur les différents champs d'entrée.

En effet, lorsque le clavier disparaît, tous les champs du formulaire sont flous. Donc, pour ma situation, cette copie a résolu le problème.

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

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

});

J'espère que ça aide. Michèle

Michèle
la source
Merci pour cette réponse. J'ai trouvé utile de résoudre un problème où le clavier Safari de l'iPad provoquait le déplacement (décalage) du curseur de zone de texte en dehors de la zone de texte.
kennbrodhagen
14

S'il existe un clavier à l'écran, la focalisation d'un champ de texte qui se trouve près du bas de la fenêtre fera défiler le champ de texte dans la vue. Il pourrait y avoir un moyen d'exploiter ce phénomène pour détecter la présence du clavier (avoir un petit champ de texte en bas de la page qui se concentre momentanément, ou quelque chose comme ça).

ianh
la source
1
C'est une idée ingénieuse. J'ai trouvé une solution similaire qui utilise également la position de défilement actuelle pour détecter le clavier virtuel.
LKM
brillant! tu as sauvé ma journée!
aztack
11

Pendant l'événement de focus, vous pouvez faire défiler la hauteur du document et, comme par magie, window.innerHeight est réduit par la hauteur du clavier virtuel. Notez que la taille du clavier virtuel est différente pour les orientations paysage et portrait, vous devrez donc le redétecter lorsqu'il change. Je déconseille de se souvenir de ces valeurs car l'utilisateur peut connecter / déconnecter un clavier Bluetooth à tout moment.

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

Notez que lorsque l'utilisateur utilise un clavier Bluetooth, le paramètre keyboardHeight est de 44, ce qui correspond à la hauteur de la barre d'outils [précédente] [suivante].

Il y a un petit scintillement lorsque vous effectuez cette détection, mais il ne semble pas possible de l'éviter.

Hafthor
la source
5
Je viens d'essayer cela dans iOS 8.2 et cela ne fonctionne pas ... a-t-il cessé de fonctionner à un moment donné pour le nouvel iOS?
Ming
1
Je n'ai pas travaillé pour moi non plus - le redimensionnement n'est pas déclenché dans iOS9.3
llamerr
8

Edit: Documenté par Apple bien que je ne puisse pas le faire fonctionner: Comportement de WKWebView avec écrans de clavier : "Dans iOS 10, les objets WKWebView correspondent au comportement natif de Safari en mettant à jour leur propriété window.innerHeight lorsque le clavier est affiché, et n'appelez pas les événements de redimensionnement "(peut peut-être utiliser le focus ou le focus plus le délai pour détecter le clavier au lieu d'utiliser le redimensionnement).

Modifier: le code suppose un clavier à l'écran, pas un clavier externe. Le laisser parce que les informations peuvent être utiles à d'autres personnes qui ne se soucient que des claviers à l'écran. Utilisez http://jsbin.com/AbimiQup/4 pour afficher les paramètres de page.

Nous testons pour voir si le document.activeElementest un élément qui montre le clavier (type d'entrée = texte, zone de texte, etc.).

Le code suivant fudge les choses pour nos besoins (bien que généralement pas 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');
};

Le code ci-dessus n'est qu'approximatif: il est incorrect pour le clavier partagé, le clavier non ancré, le clavier physique. Selon le commentaire en haut, vous pourrez peut-être faire un meilleur travail que le code donné sur Safari (depuis iOS8?) Ou WKWebView (depuis iOS10) en utilisantwindow.innerHeight propriété.

J'ai trouvé des échecs dans d'autres circonstances: par exemple, donnez le focus à l'entrée puis allez à l'écran d'accueil puis revenez à la page; L'iPad ne devrait pas réduire la taille de la fenêtre d'affichage; les anciens navigateurs IE ne fonctionnaient pas, Opera ne fonctionnait pas car Opera gardait le focus sur l'élément après la fermeture du clavier.

Cependant, la réponse étiquetée (changer le scrolltop pour mesurer la hauteur) a des effets secondaires désagréables sur l'interface utilisateur si la fenêtre peut zoomer (ou forcer le zoom activé dans les préférences). Je n'utilise pas l'autre solution suggérée (changer le scrolltop) car sur iOS, lorsque la fenêtre peut zoomer et faire défiler jusqu'à l'entrée focalisée, il y a des interactions boguées entre le défilement et le zoom et la mise au point (qui peuvent laisser une entrée juste focalisée en dehors de la fenêtre - non visible).

Robocat
la source
En fonction des navigateurs, innerHeight pour détecter les coupures en plein écran lorsque certains éléments sont positionnés de manière absolue. Pas fiable du tout.
Udo
5

Uniquement testé sur Android 4.1.1:

L'événement de flou n'est pas un événement fiable pour tester le clavier de haut en bas car l'utilisateur a la possibilité de masquer explicitement le clavier, ce qui ne déclenche pas d'événement de flou sur le champ qui a provoqué l'affichage du clavier.

l'événement de redimensionnement fonctionne cependant comme un charme si le clavier monte ou descend pour une raison quelconque.

café:

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

se déclenche à chaque fois que le clavier est affiché ou masqué pour une raison quelconque.

Notez cependant que dans le cas d'un navigateur Android (plutôt que d'une application), il existe une barre d'URL rétractable qui ne déclenche pas de redimensionnement lorsqu'il est rétracté mais qui modifie la taille de la fenêtre disponible.

user1650613
la source
+1 pour l'événement de flou qui ne se déclenche pas lors de la suppression manuelle du clavier. Le redimensionnement est une bonne idée et cela fonctionnerait bien pour les appareils Android.
Ankit Garg
Peut confirmer que cela fonctionne à la fois sur un iPhone 5 (iOS 6.0.2) et un iPad 3 (iOS 6.0).
Diego Agulló
1
Juste testé sur Chrome 41 sur iOS6 sur CrossBrowserTesting - le redimensionnement n'est pas déclenché par l'apparition ou la disparition du clavier virtuel.
Dan Dascalescu
4

Au lieu de détecter le clavier, essayez de détecter la taille de la fenêtre

Si la hauteur de la fenêtre a été réduite et que la largeur est toujours la même, cela signifie que le clavier est activé. Sinon, le clavier est désactivé, vous pouvez également ajouter à cela, tester si un champ de saisie est sur le focus ou non.

Essayez ce code par exemple.

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
la source
2
Cela ne semble plus fonctionner sous iOS 8. Le clavier recouvre le contenu et, dans de nombreux cas, le contenu défile vers le bas en masquant les champs de saisie initialement ciblés.
Rick Strahl le
3
window height renvoie la hauteur, y compris le clavier depuis iOS 7, dans la fenêtre IOS6, la hauteur change lorsque le clavier s'ouvre.
Michiel
1
Notez que la hauteur change également lorsque la barre d'adresse supérieure glisse dans et hors de l'écran lors du défilement. Vous devriez ajouter un changement de hauteur minimum de, je dirais, 200px (non testé).
oriadam
1

Cette solution mémorise la position de défilement

    var currentscroll = 0;

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

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

        $(window).scrollTop(currentscroll);

        }
    });
WebsterDévelopper
la source
1

Essaye celui-là:

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
la source
1

Comme indiqué dans les réponses précédentes, quelque part, la variable window.innerHeight est mise à jour correctement maintenant sur iOS10 lorsque le clavier apparaît et comme je n'ai pas besoin de la prise en charge des versions antérieures, j'ai proposé le hack suivant qui pourrait être un peu plus facile que celui discuté. "solutions".

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

alors vous pouvez utiliser:

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

pour vérifier si le clavier est visible. Je l'utilise depuis un moment maintenant dans mon application Web et cela fonctionne bien, mais (comme toutes les autres solutions) vous pourriez trouver une situation où cela échoue parce que la taille "attendue" n'est pas mise à jour correctement ou quelque chose du genre.

Couler
la source
J'espérais que c'était le cas, mais non, il n'est pas mis à jour.
Sam Saffron le
0

J'ai fait quelques recherches, et je n'ai rien trouvé de concret pour "sur le clavier affiché" ou "sur le clavier ignoré". Consultez la liste officielle des événements pris en charge . Voir également la note technique TN2262 pour iPad. Comme vous le savez probablement déjà, il y a un événement corporelonorientationchange vous pouvez câbler pour détecter le paysage / portrait.

De même, mais une supposition sauvage ... avez-vous essayé de détecter le redimensionnement? Les modifications de la fenêtre peuvent déclencher cet événement indirectement à partir du clavier affiché / masqué.

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

Ce qui alerterait simplement la nouvelle hauteur sur tout événement de redimensionnement ...

slf
la source
10
Malheureusement, dans mes tests, le clavier n'a pas déclenché l'événement de redimensionnement.
LKM
0

Je n'ai pas essayé cela moi-même, donc c'est juste une idée ... mais avez-vous essayé d'utiliser des requêtes multimédias avec CSS pour voir quand la hauteur de la fenêtre change et ensuite changer la conception pour cela? J'imagine que Safari mobile ne reconnaît pas le clavier comme faisant partie de la fenêtre, ce qui, espérons-le, fonctionnerait.

Exemple:

@media all and (height: 200px){
    #content {height: 100px; overflow: hidden;}
}
Janae
la source
2
Idée très intelligente. Malheureusement, dans mes tests, l'affichage du clavier n'a pas affecté les valeurs de hauteur utilisées pour évaluer les requêtes multimédias.
LKM
Je peux confirmer: hauteur: 250px a fonctionné pour moi (sur Android, au moins).
WoodrowShigeru
0

Le problème est que, même en 2014, les appareils gèrent les événements de redimensionnement d'écran, ainsi que les événements de défilement, de manière incohérente lorsque le clavier virtuel est ouvert.

J'ai constaté que, même si vous utilisez un clavier Bluetooth, iOS en particulier déclenche d'étranges bogues de mise en page; donc au lieu de détecter un clavier logiciel, j'ai juste dû cibler des appareils très étroits et dotés d'écrans tactiles.

J'utilise des requêtes multimédias (ou window.matchMedia ) pour la détection de largeur et Modernizr pour la détection d'événements tactiles.

pixelbandito
la source
0

Il est peut-être plus facile d'avoir une case à cocher dans les paramètres de votre application où l'utilisateur peut basculer `` clavier externe connecté? ''.

En petits caractères, expliquez à l'utilisateur que les claviers externes ne sont actuellement pas détectables dans les navigateurs actuels.

Ian White
la source
1
L'ajout d'une bascule comme celle-ci est un dernier recours qui ne devrait pas du tout être considéré comme acceptable à moins qu'il n'y ait aucune autre solution qui ne cassera pas l'application. Ce n'est pas quelque chose qui devrait empêcher la production d'une application fonctionnelle.
Adam Leggett
-2

Eh bien, vous pouvez détecter quand vos zones de saisie ont le focus et vous connaissez la hauteur du clavier. Il existe également du CSS pour obtenir l'orientation de l'écran, donc je pense que vous pouvez le pirater.

Vous voudrez cependant gérer le cas d'un clavier physique d'une manière ou d'une autre.

stuntmouse
la source