web-dev-qa-db-fra.com

iPad Web App: Détecter un clavier virtuel à l'aide de JavaScript dans Safari?

J'écris une application Web pour iPad ( pas une application standard de l'App Store - elle est écrite en HTML, CSS et JavaScript). Étant donné que le clavier occupe une grande partie de l'écran, il serait judicieux 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 a été 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'affectait pas non plus la hauteur ni la hauteur de défilement des éléments DOM, et je n'ai trouvé aucun événement propriétaire ni aucune propriété indiquant si le clavier était visible.

137
LKM

J'ai trouvé une solution qui fonctionne, même si c'est un peu moche. Cela ne fonctionnera pas non plus dans toutes les situations, mais cela fonctionne pour moi. Étant donné que 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 configure 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 comment 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, du moins lorsqu’il est exécuté dans le simulateur, l’iPad défile à nouveau de manière visible (bien que rapide). Pourtant, cela fonctionne, au moins dans certaines situations spécifiques.

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

53
LKM

Vous pouvez utiliser l'événement focusout pour détecter la suppression 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 la suppression du clavier. 

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

Sans cet extrait de code, le conteneur d'applications est resté dans la position de défilement ascendant jusqu'à l'actualisation de la page.

28

peut-être qu'une solution légèrement meilleure consiste à lier (avec jQuery dans mon cas) l'événement "flou" sur les divers champs de saisie.

Ceci parce que lorsque le clavier disparaît, tous les champs de formulaire sont flous ..__ Donc, dans mon cas, cela a résolu le problème.

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

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

});

espérons que cela aide . Michele

15
Michele

S'il existe un clavier à l'écran, la focalisation d'un champ de texte situé près du bas de la fenêtre d'affichage fera que Safari le fera défiler. Il pourrait y avoir un moyen d’exploiter ce phénomène pour détecter la présence du clavier (avoir un minuscule champ de texte au bas de la page qui obtient le focus momentanément, ou quelque chose du genre).

14
ianh

Pendant l'événement de focus, vous pouvez faire défiler la hauteur du document et, comme par magie, le window.innerHeight est réduit de la hauteur du clavier virtuel. Notez que la taille du clavier virtuel est différente pour les orientations paysage ou portrait; vous devrez donc le redétecter lorsqu'il changera. Je vous déconseille de vous rappeler 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, la valeur de keyboardHeight est 44, c'est-à-dire la hauteur de la barre d'outils [précédente] [suivante].

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

11
Hafthor

Edit: documenté par Apple bien que je ne sois pas parvenu à le faire fonctionner: WKWebView Comportement avec les affichages du 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 ne pas appeler les événements de redimensionnement "(vous pouvez peut-être utiliser le focus ou le focus avec délai pour détecter le clavier au lieu d’utiliser le redimensionnement).

Modifier: le code suppose un clavier à l'écran et non un clavier externe. Le laisser parce que les informations peuvent être utiles aux autres personnes qui s’intéressent uniquement aux claviers à l’écran. Utilisez http://jsbin.com/AbimiQup/4 pour afficher les paramètres de page.

Nous testons pour voir si le document.activeElement est un élément qui montre le clavier (type d’entrée = text, textarea, etc.).

Le code suivant fausse les choses pour nos besoins (bien que ce ne soit 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 ne convient pas pour un clavier divisé, un clavier non amarré ou un 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 utilisant la propriété window.innerHeight

J'ai trouvé des échecs dans d'autres circonstances: par exemple: concentrez-vous sur l'entrée puis allez à l'écran d'accueil puis revenez à la page; iPad ne devrait pas rendre la fenêtre d'affichage plus petite; les anciens navigateurs IE ne fonctionneraient pas, Opera ne fonctionnait pas car Opera gardait le focus sur les éléments après la fermeture du clavier.

Cependant, la réponse balisée (changer le scrolltop pour mesurer la hauteur) a des effets secondaires désagréables si la fenêtre est zoomable (ou que le zoom forcé est activé dans les préférences). Je n'utilise pas l'autre solution suggérée (changement de scrolltop) car sur iOS, lorsque la fenêtre d'affichage est zoomable et que le défilement s'effectue en une entrée focalisée, il existe des interactions erronées entre le défilement, le zoom et la mise au point (qui peuvent laisser une entrée uniquement focalisée en dehors de la fenêtre d'affichage - pas visible).

8
robocat

Seulement testé sur Android 4.1.1:

blur event 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 un événement de flou sur le champ à l'origine de l'affichage du clavier.

l'événement redimensionner 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 toutefois 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 le redimensionnement lorsqu'elle est rétractée, mais qui modifie la taille de la fenêtre disponible.

5
user1650613

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 identique, cela signifie que le clavier est activé . Si le clavier est désactivé, vous pouvez également y ajouter, tester si un champ de saisie est actif ou ne pas.

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

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 précédentes, j'ai proposé le piratage suivant qui pourrait être un peu plus facile que les "solutions" discutées.

//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 certain temps déjà dans mon application Web et cela fonctionne bien, mais (comme toutes les autres solutions), il est possible que la situation échoue car la taille "attendue" n'est pas correctement mise à jour ou quelque chose du genre.

1
Flow

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

Cette solution se souvient de 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);

        }
    });
1

Dans le navigateur Safari, lorsqu'un élément est placé en bas de l'écran et que le clavier virtuel est visible, cette position augmente d'environ 1 px. Vous pouvez exploiter cela.

Supposons qu'il s'agisse d'un appareil mobile en mode paysage

<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

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

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

0
Ian White

Je n'ai pas essayé cela moi-même, alors c'est juste une idée ... mais avez-vous essayé d'utiliser des requêtes de média avec CSS pour voir quand la hauteur de la fenêtre change, puis en modifier la conception? J'imagine que Safari mobile ne reconnaît pas le clavier comme faisant partie de la fenêtre, ce qui fonctionnerait si tout va bien.

Exemple:

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

Le problème est que, même en 2014, les périphériques 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 logiciel 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. au lieu de détecter un clavier logiciel, je n’ai donc plus qu’à cibler des périphériques très étroits et dotés d’écrans tactiles.

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

0
pixelbandito

J'ai fait quelques recherches et je n'ai rien trouvé de concret pour un "clavier affiché" ou un "clavier désactivé". Voir la liste officielle des événements soutenus . Voir également Note technique TN2262 pour iPad. Comme vous le savez probablement déjà, il existe un événement de corps onorientationchange que vous pouvez utiliser pour détecter paysage/portrait.

De la même façon, mais une supposition sauvage ... avez-vous essayé de détecter le redimensionnement? Les modifications de la fenêtre d'affichage 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 ....

0
slf