Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I prevent wild scrolling when a fixed position text input form field gains focus?

I have a mobile web app that displays a dialog box within a position: fixed element overlaying the entire screen. No matter how much content is displayed or how far the page has scrolled, the overlay element dims the page content and the dialog appears on top of it all. There is an <input type="search /> field in the dialog.

On Android, when the user taps the input field to give it focus, some pretty erratic behavior often (but not always) ensues:

  • The soft keyboard appears, then immediately disappears.
  • The underlying page (covered by the overlay) scrolls by a few dozen pixels, sometimes up, sometimes down, sometimes in both directions, and sometimes repeatedly. This also happens when the input field loses focus.
  • The overlay element momentarily shifts a few pixels down and right, revealing the underlying content along the top and left edges of the screen. (It returns to place less than a second later.)

This is on Android 2.3.4 with the built-in browser. It may also occur on other Android versions.

The page constructed something like this: (Keep in mind that some of these quirks only show up when the page contains enough content to allow scrolling.)

<head>
  <style type="text/css">
    #menuoverlay {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 10;
    background-color: rgba(0,0,0,.75);
    text-align: center;
    }
    #menu {
    display: inline-block;
    margin-top: 1em;
    border: 3px solid #02b;
    border-radius: 8px;
    padding: 1ex;
    background-color: black;
    color: silver;
    }
  </style>
</head>

<body>
  <div id="menuoverlay">
    <div id="menu">
      ...menu items...
      ...menu items...
      <form action=""><input type="search"/></form>
    </div>
  </div>

  <div id="content">
    ...lots of stuff...
    ...lots of stuff...
    ...lots of stuff...
  </div>
<body>

Has anyone else seen this behavior? Have you found a way to prevent it?


Scouring the web led me to a few reports of similar problems. Here's a summary of what I have read so far:

  • On many Android devices, the built-in browser apparently does not use WebKit's input fields the way one would expect. Instead, when the html-defined fields gain focus, the browser attempts to cover them with new text widgets that it creates separately.
  • These duplicate text fields often ignore css styling, creating an effect where the fields look styled until they gain focus, and then suddenly revert to the system default style.
  • The Android browser often fails to position these duplicate fields correctly. When misplaced, they are sometimes visible, but other times only move the focus targets without any visual indication that they have moved.
  • These problems often show up when an input field's ancestor element has styles like transform: translate3d(), -webkit-backface-visibility, or position: fixed.

Here are some relevant links:

Android Browser textarea scrolls all over the place, is unusable

How can I style an HTML INPUT tag so it maintains CSS when focused on Android 2.2+?

Android 2.2/2.3’s Native Browser and Fixed Positioning

like image 443
ʇsәɹoɈ Avatar asked May 10 '12 21:05

ʇsәɹoɈ


1 Answers

I managed to prevent the momentary overlay shift by setting outline: none on my input element.

I got the wild page scrolling and disappearing keyboard to settle down by setting overflow: hidden on the html body when showing the dialog, and then removing it again when hiding the dialog. This has a nasty side effect of resetting the page's scroll position, so I save it and restore it as necessary, and wrap all this hackery in conditionals so it only runs on Android. (I hope my Android users won't be too distracted when they see the page contents change beneath the semi-transparent overlay while the dialog is open.)

Interestingly, I was also able to prevent the wild page scrolling by catching the touchstart event on my overlay element and calling preventDefault(). This makes me wonder if all this madness was caused by a sequence of events like this:

  • touchstart bubbles up to the document root
  • browser sees touchstart where the duplicate input field was placed
  • browser gives focus to the input field
  • soft keyboard appears to allow typing in the input field
  • viewport gets resized to accommodate the soft keyboard
  • resized viewport during touch event looks like a touch drag to the browser
  • spurious drag causes the page to scroll and dismisses the keyboard

I didn't end up catching touchstart to solve the problem, because it prevented the input field from gaining focus, and calling focus() from javascript just didn't work. I have read that the Android browser disables the focus() method on input fields, which would explain this behavior. Perhaps it does this because it wouldn't work with the duplicate text widgets it creates over the html-defined fields.

like image 60
ʇsәɹoɈ Avatar answered Sep 22 '22 02:09

ʇsәɹoɈ