Prevent scrolling on mobile browser, without preventing input focusing

Bobby picture Bobby · Sep 18, 2012 · Viewed 21k times · Source

I use preventDefault() on touchstart on the document to prevent scrolling on a page. Unfortunately this prevents too much. The user can no longer give focus to an input (and open the soft keyboard). Using jQuery focus(), I can give the focus to the input on a touchstart. This opens the soft keyboard on iOS (most of the time), but never on Android.

Background:

I have a webpage that I want to make as much like a mobile app as possible. I'm only really concerned with Android and iOS, but any form factor. I start by making the content in the page exactly the same size as the screen size. This is nice until the user puts their finger on the page. I need to prevent user scrolling. The following code accomplishes this, but in slightly different ways on the two operating systems.

$(document).on('touchstart', function(e) {
    e.preventDefault();
});

On iOS, this prevents the elastic scrolling: where a user can reveal a texture behind the webpage by attempting to scroll off the page. The feature is nice for a regular webpage, but in my case it detracts from the UX.

On Android, prevents the revelation of a handy hack that I use to hide the address bar. Android browsers start with the address bar visible, but as the user scrolls down the page, it disappears. To programmatically force the browser hide the address bar, simply add this line of code to your function called at load.

$('html, body').scrollTop(1);

This is a hacky (but also the only) way to tell the android browser that we have scrolled, and the address bar is no longer necessary.

Without the preventDefault on the document, the Android browser will allow scrolling and the address bar can be revealed.

So, both OS's have a good reason to have this preventDefault() called on every touchstart on the document, but it prevents too much. Tapping on an input field does nothing. Using a call to jQuery focus() can help, but only opens the soft keyboard on iOS, not Android.

$('input').on('touchstart', function() {
    $(this).focus();
});

How can I prevent the page from scrolling, but use the browser native functionality for giving focus to input fields?

Note: This code

$(document).on('touchstart', function(e) {
    if (e.target.nodeName !== 'INPUT') {
        e.preventDefault();
    }
});

is flawed because the user can still scroll the page as long as the initial touch originates from within the input field.

Answer

Bobby picture Bobby · Sep 18, 2012

I actually solved this problem on another project, forgot about it, and remembered it most of the way through typing this up.

They key is to just do it on touchmove.

$(document).on('touchmove', function(e) {
    e.preventDefault();
});

However, preventDefault on touchstart does all kinds of nice things like preventing the image save menu on a gesture enabled slideshow. My projects also include this.

$(document).on('touchstart', function(e) {
    if (e.target.nodeName !== 'INPUT') {
        e.preventDefault();
    }
});

If anyone has some suggestions on additional content or how to reformat this so that it can reach a greater audience that would be great. I haven't ever seen the content I have here all in one place, so I felt that it needed to be on SO.