Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android does not correctly scroll on input focus if not body element

When a mobile browser brings up a keyboard it tries to move the scrollbars so that the input is still in view.

On iOS Safari it seems to do this properly by finding the nearest scrolling parent.

On Android native or Chrome mobile browser it seems to just try the body element and then gives up, so the focused input is hidden beneath the keyboard.

 How to break it

Set overflow-y: hidden on the body element. Create a scrollable container and put a form in there.

When you select an element near the bottom of your screen it will be obscured by the keyboard.

Demo

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>

Any ideas how to fix this? Will it require some browser detection and messy hacks?

like image 952
Dominic Avatar asked May 20 '14 10:05

Dominic


People also ask

How can I make my Android scroll smoother?

Uninstall bloatware and unused apps Additionally, too much bloatware and unused apps can cripple your device's performance. In case you have apps you're not using or many preinstalled apps on your device, consider uninstalling or disabling them to make your phone run faster. Gently scroll through the apps.

Does focus scroll the page?

An element can be focused by either using the autofocus="true" attribute or calling the element. focus() method. In both cases, the browser will automatically scroll the element into the viewport.

Why is my page not scrolling?

Your website might not be scrolling if there is a problem with your browser (Google Chrome, Safari, etc.), website code, or your computer hardware. You can investigate & fix the issue by restarting the browser, looking at the site code, deleting cookies, extensions, and/or caches.


3 Answers

This is a bug in the Android native browser. By the way, the input scrolls into the view after a character is typed on the soft keyboard.

The following code snippet placed somewhere in the page should help:

if(/Android 4\.[0-3]/.test(navigator.appVersion)){
   window.addEventListener("resize", function(){
      if(document.activeElement.tagName=="INPUT"){
         window.setTimeout(function(){
            document.activeElement.scrollIntoViewIfNeeded();
         },0);
      }
   })
}
like image 110
Serge Avatar answered Oct 16 '22 17:10

Serge


The answer from Serge is great but I had a few modifications that improved it for me.

The problem appeared on Android 6 for me as well so I added it to the check and I needed the fix to work for textareas as well as inputs.

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

If anyone needs the fix in Angular 1, here is what I used there.

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();
                });
            }
        });
    }
});
like image 9
Zack Huston Avatar answered Oct 16 '22 15:10

Zack Huston


Offering slight revision if it saves anyone some time:

  • No need to specify an Android version # (less likely to break when your user gets Android 7.0+)
  • No need to wrap in a setTimeOut
  • MDN advises against .scrollIntoViewIfNeeded bc of browser incompatibility => .scrollIntoView is a workable substitute with slightly more browser compatibility

    if(/Android/.test(navigator.appVersion)) {
       window.addEventListener("resize", function() {
         if(document.activeElement.tagName=="INPUT" || document.activeElement.tagName=="TEXTAREA") {
           document.activeElement.scrollIntoView();
         }
      })
    } 
    
like image 6
Derek Mueller Avatar answered Oct 16 '22 16:10

Derek Mueller