web-dev-qa-db-fra.com

Android ne fait pas défiler correctement le focus d'entrée si ce n'est pas l'élément body

Lorsqu'un navigateur mobile affiche un clavier, il tente de déplacer les barres de défilement de sorte que l'entrée soit toujours visible.

Sur iOS Safari, il semble que cela fonctionne correctement en recherchant le parent le plus proche / qui défile.

Sur le navigateur Android natif ou le navigateur mobile Chrome, il semble simplement essayer l’élément body, puis abandonner, de sorte que l’entrée ciblée est masquée sous le clavier.

 Comment le casser

Définissez overflow-y: hidden sur l'élément body. Créez un conteneur avec défilement et placez un formulaire.

Lorsque vous sélectionnez un élément près du bas de votre écran, celui-ci est masqué par le clavier.

Démo

http://dominictobias.com/Android-scroll-bug/

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"/>
    <title>Android scroll/focus bug</title>
    <style>
    html, body {
        margin: 0;
        padding: 0;
        height: 100%;
        overflow: hidden;
    }
    .scroll {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        overflow-y: scroll;
    }
    input {
        margin-bottom: 20px;
        width: 100%;
    }
    </style>
</head>
<body>

    <div class="scroll">
        <input type="text" value="Input 1">
        <input type="text" value="Input 2">
        <input type="text" value="Input 3">
        <input type="text" value="Input 4">
        <input type="text" value="Input 5">
        <input type="text" value="Input 6">
        <input type="text" value="Input 7">
        <input type="text" value="Input 8">
        <input type="text" value="Input 9">
        <input type="text" value="Input 10">
        <input type="text" value="Input 11">
        <input type="text" value="Input 12">
        <input type="text" value="Input 13">
        <input type="text" value="Input 14">
        <input type="text" value="Input 15">
        <input type="text" value="Input 16">
        <input type="text" value="Input 17">
        <input type="text" value="Input 18">
        <input type="text" value="Input 19">
        <input type="text" value="Input 20">
    </div>

</body>
</html>

Une idée de comment réparer ça? Cela nécessitera-t-il une détection du navigateur et des hacks compliqués?

13
Dominic

Ceci est un bug dans le navigateur natif Android. En passant, l'entrée défile dans la vue après la saisie d'un caractère sur le clavier logiciel.

L'extrait de code suivant placé quelque part dans la page devrait vous aider:

if(/Android 4\.[0-3]/.test(navigator.appVersion)){
   window.addEventListener("resize", function(){
      if(document.activeElement.tagName=="INPUT"){
         window.setTimeout(function(){
            document.activeElement.scrollIntoViewIfNeeded();
         },0);
      }
   })
}
45
Serge

La réponse de Serge est excellente, mais j'ai eu quelques modifications qui l'ont améliorée pour moi. 

Le problème est apparu sur Android 6 pour moi aussi, alors je l'ai ajouté à la vérification et j'avais besoin de la solution pour fonctionner avec textareas ainsi que des entrées.

if(/Android [4-6]/.test(navigator.appVersion)) {
   window.addEventListener("resize", function() {
      if(document.activeElement.tagName=="INPUT" || document.activeElement.tagName=="TEXTAREA") {
         window.setTimeout(function() {
            document.activeElement.scrollIntoViewIfNeeded();
         },0);
      }
   })
}

Si quelqu'un a besoin du correctif dans Angular 1, voici ce que j'ai utilisé ici.

angular.module('<module name>').run(function ($window, $timeout) {
    if(/Android [4-6]/.test($window.navigator.appVersion)){
        $window.addEventListener("resize", function(){
            if(document.activeElement.tagName=="INPUT" || document.activeElement.tagName=="TEXTAREA"){
                $timeout(function() {
                    document.activeElement.scrollIntoViewIfNeeded();
                });
            }
        });
    }
});
9
Zack Huston

Offrir une légère révision si cela fait gagner du temps à quiconque:

  • Nul besoin de spécifier une version Android # (moins de risques de rupture lorsque votre utilisateur utilise Android 7.0+)
  • Pas besoin d'envelopper dans un setTimeOut
  • MDN déconseille .scrollIntoViewIfNeeded bc d'incompatibilité de navigateur => .scrollIntoView est un substitut utilisable avec une compatibilité de navigateur légèrement supérieure

    if(/Android/.test(navigator.appVersion)) {
       window.addEventListener("resize", function() {
         if(document.activeElement.tagName=="INPUT" || document.activeElement.tagName=="TEXTAREA") {
           document.activeElement.scrollIntoView();
         }
      })
    } 
    
1
Derek Mueller

En regardant cela légèrement différemment, le bogue semble être causé par la fonctionnalité Suggestions du navigateur. Comme je ne veux pas vraiment les suggestions de toute façon que j'ai utilisées:

if(/Android/.test(navigator.appVersion)){
  $('input[type="text"]').attr('autocomplete', "off");
}

ce qui donne une expérience beaucoup plus lisse.

0
Ben Trewern