Skip to content

Instantly share code, notes, and snippets.

@searls
Last active April 6, 2022 19:47
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save searls/d6fd21a57b7c70be12f65beb17bb6149 to your computer and use it in GitHub Desktop.
Save searls/d6fd21a57b7c70be12f65beb17bb6149 to your computer and use it in GitHub Desktop.

The problem

When a MobileSafari keyboard-receing input (e.g. an input[type=text]) receives focus in Safari, the system will try to verify whether there is ample room to bring up the soft keyboard without needing to scroll. The amount of wiggle room needed for Safari to consider available scroll height "ample" seems to increase with every major iOS release (surely to work around the very real problem of focus being granted to inputs that are subsequently oscured by the keyboard).

However, for "app-ey" web sites that carefully place each text input in a relatively fixed layout, this can be a maddening arms of scooching up my content more and more with each iOS release to avoid awkward jutters every time the keyboard is shown (example video here. In practice, it seems like the input's scrollHeight can only really be about the equivalent of 15vh before you're likely to get an automated scrolling whiplash from the soft keyboard, which isn't much space at all.

The hack

With iOS 12, my various hacks that took the input out of flow by dynamically changing it to position: absolute or position: fixed stopped working. So, in order to prevent MobileSafari's keyboard from scrolling the page content when it thinks there isn't room, this approach uses CSS transform to make the input so far above the top of the page that Safari can't even try to scroll to it. (video of it working)

const focusKeyboardWithoutPushingPage = (el) => {
  el.style.transform = 'TranslateY(-8000px)'
  el.focus()
  setTimeout(() => {
    el.style.transform = 'none'
  }, 0)
}

Then, I can programmatically focus on an input with:

focusKeyboardWithoutPushingPage(document.querySelector('#something'))

And even with a timeout of 0 milliseconds, MobileSafari will bring the keyboard up without attempting to scroll.

For user-initiated focus events, you can just bind to ontouchstart and:

document.querySelector('#something').addEventListener('ontouchstart', (e) => { focusKeyboardWithoutPushingPage(e.target) })

Even though the ontouchstart handler is invoking focus() itself, everything works fine (as of iOS 12.1.2)

@f-liva
Copy link

f-liva commented Nov 22, 2019

This is a good solution when the input is at the top of the page but, if you are at a different page height (as a result of a scroll, for example), at the focus of the field the page is brought back to the top in scroll position 0, moving the user from the point of the page where he was.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment