Disabling android's chrome pull-down-to-refresh feature

AndroidHtmlGoogle ChromeMeta Tags

Android Problem Overview


I've created a small HTML5 web application for my company.

This application displays a list of items and everything works fine.

The application is mainly used on android phones and Chrome as browser. Also, the site is saved on the home screen so Android manage the whole thing as an app (using a WebView I guess).

Chrome Beta (and I think also the Android System WebView) has introduced a "pull down to refresh" feature (See this link for example).

This is an handy feature but I was wondering if it can be disabled with some meta tag (or javascript stuff) because the refresh can be easily triggered by the user while navigating the list and the whole app is reloaded.

Also this is a feature not needed by the application.

I know that this feature is still available only in Chrome beta, but I have the sensation that this is landing on the stable app, too.

Thank you!

Edit: I've uninstalled Chrome Beta and the link pinned to the home screen now opens with the stable Chrome. So the pinned links starts with Chrome and not with a webview.

Edit: today (2015-03-19) the pull-down-to-refresh has come to the stable chrome.

Edit: from @Evyn answer I follow this link and got this javascript/jquery code that work.

var lastTouchY = 0;
var preventPullToRefresh = false;

$('body').on('touchstart', function (e) {
    if (e.originalEvent.touches.length != 1) { return; }
    lastTouchY = e.originalEvent.touches[0].clientY;
    preventPullToRefresh = window.pageYOffset == 0;
});

$('body').on('touchmove', function (e) {
    var touchY = e.originalEvent.touches[0].clientY;
    var touchYDelta = touchY - lastTouchY;
    lastTouchY = touchY;
    if (preventPullToRefresh) {
        // To suppress pull-to-refresh it is sufficient to preventDefault the first overscrolling touchmove.
        preventPullToRefresh = false;
        if (touchYDelta > 0) {
            e.preventDefault();
            return;
        }
    }
});

As @bcintegrity pointed out, I hope for a site manifest solution (and/or a meta-tag) in the future.

Moreover suggestions for the code above are welcome.

Android Solutions


Solution 1 - Android

The default action of the pull-to-refresh effect can be effectively prevented by doing any of the following :

  1. preventDefault’ing some portion of the touch sequence, including any of the following (in order of most disruptive to least disruptive):
  • a. The entire touch stream (not ideal).
  • b. All top overscrolling touchmoves.
  • c. The first top overscrolling touchmove.
  • d. The first top overscrolling touchmove only when 1) the initial touchstart occurred when the page y scroll offset was zero and 2) the touchmove would induce top overscroll.
  1. Applying “touch-action: none” to touch-targeted elements, where appropriate, disabling default actions (including pull-to-refresh) of the touch sequence.
  2. Applying “overflow-y: hidden” to the body element, using a div for scrollable content if necessary.
  3. Disabling the effect locally via chrome://flags/#disable-pull-to-refresh-effect).

See more

Solution 2 - Android

Simple solution for 2019+

Chrome 63 has added a css property to help out with exactly this. Have a read through this guide by Google to get a good idea of how you can handle it.

Here is their TL:DR

> The CSS overscroll-behavior property allows developers to override the > browser's default overflow scroll behavior when reaching the > top/bottom of content. Use cases include disabling the pull-to-refresh > feature on mobile, removing overscroll glow and rubberbanding effects, > and preventing page content from scrolling when it's beneath a > modal/overlay.

To get it working, all you have to add is this in your CSS:

body {
  overscroll-behavior: contain;
}

It is also only supported by Chrome, Edge and Firefox for now but I'm sure Safari will add it soon as they seem to be fully onboard with service workers and the future of PWA's.

Solution 3 - Android

At the moment you can only disable this feature via chrome://flags/#disable-pull-to-refresh-effect - open directly from your device.

You could try to catch touchmove events, but chances are very slim to achieve an acceptable result.

Solution 4 - Android

I use MooTools, and I have created a Class to disable refresh on a targeted element, but the crux of it is (native JS):

var target = window; // this can be any scrollable element
var last_y = 0;
target.addEventListener('touchmove', function(e){
	var scrolly = target.pageYOffset || target.scrollTop || 0;
	var direction = e.changedTouches[0].pageY > last_y ? 1 : -1;
	if(direction>0 && scrolly===0){
		e.preventDefault();
	}
	last_y = e.changedTouches[0].pageY;
});

All we do here is find the y direction of the touchmove, and if we are moving down the screen and the target scroll is 0, we stop the event. Thus, no refresh.

This means we are firing on every 'move', which can be expensive, but is the best solution I have found so far ...

Solution 5 - Android

After many hours of trying, this solution works for me

$("html").css({
    "touch-action": "pan-down"
});

Solution 6 - Android

You can try this

body {
  /* Disables pull-to-refresh but allows overscroll glow effects. */
  overscroll-behavior-y: contain;
}

example :

https://ebidel.github.io/demos/chatbox.html

full doc https://developers.google.com/web/updates/2017/11/overscroll-behavior

Solution 7 - Android

AngularJS

I have successfully disabled it with this AngularJS directive:

//Prevents "pull to reload" behaviour in Chrome. Assign to child scrollable elements.
angular.module('hereApp.directive').directive('noPullToReload', function() {
    'use strict';

    return {
        link: function(scope, element) {
            var initialY = null,
                previousY = null,
                bindScrollEvent = function(e){
                    previousY = initialY = e.touches[0].clientY;

                    // Pull to reload won't be activated if the element is not initially at scrollTop === 0
                    if(element[0].scrollTop <= 0){
                        element.on("touchmove", blockScroll);
                    }
                },
                blockScroll = function(e){
                    if(previousY && previousY < e.touches[0].clientY){ //Scrolling up
                        e.preventDefault();
                    }
                    else if(initialY >= e.touches[0].clientY){ //Scrolling down
                        //As soon as you scroll down, there is no risk of pulling to reload
                        element.off("touchmove", blockScroll);
                    }
                    previousY = e.touches[0].clientY;
                },
                unbindScrollEvent = function(e){
                    element.off("touchmove", blockScroll);
                };
            element.on("touchstart", bindScrollEvent);
            element.on("touchend", unbindScrollEvent);
        }
    };
});

It's safe to stop watching as soon as the user scrolls down, as the pull to refresh has no chance of being triggered.

Likewise, if scrolltop > 0, the event won't be triggered. In my implementation, I bind the touchmove event on touchstart, only if scrollTop <= 0. I unbind it as soon as the user scrolls down (initialY >= e.touches[0].clientY). If the user scrolls up (previousY < e.touches[0].clientY), then I call preventDefault().

This saves us from watching scroll events needlessly, yet blocks overscrolling.

jQuery

If you are using jQuery, this is the untested equivalent. element is a jQuery element:

var initialY = null,
    previousY = null,
    bindScrollEvent = function(e){
        previousY = initialY = e.touches[0].clientY;

        // Pull to reload won't be activated if the element is not initially at scrollTop === 0
        if(element[0].scrollTop <= 0){
            element.on("touchmove", blockScroll);
        }
    },
    blockScroll = function(e){
        if(previousY && previousY < e.touches[0].clientY){ //Scrolling up
            e.preventDefault();
        }
        else if(initialY >= e.touches[0].clientY){ //Scrolling down
            //As soon as you scroll down, there is no risk of pulling to reload
            element.off("touchmove");
        }
        previousY = e.touches[0].clientY;
    },
    unbindScrollEvent = function(e){
        element.off("touchmove");
    };
element.on("touchstart", bindScrollEvent);
element.on("touchend", unbindScrollEvent);

Naturally, the same can also be achieved with pure JS.

Solution 8 - Android

Simple Javascript

I implemented using standard javascript. Simple and easy to implement. Just paste and it works fine.

<script type="text/javascript">         //<![CDATA[
     window.addEventListener('load', function() {
          var maybePreventPullToRefresh = false;
          var lastTouchY = 0;
          var touchstartHandler = function(e) {
            if (e.touches.length != 1) return;
            lastTouchY = e.touches[0].clientY;
            // Pull-to-refresh will only trigger if the scroll begins when the
            // document's Y offset is zero.
            maybePreventPullToRefresh =
                window.pageYOffset == 0;
          }

          var touchmoveHandler = function(e) {
            var touchY = e.touches[0].clientY;
            var touchYDelta = touchY - lastTouchY;
            lastTouchY = touchY;

            if (maybePreventPullToRefresh) {
              // To suppress pull-to-refresh it is sufficient to preventDefault the
              // first overscrolling touchmove.
              maybePreventPullToRefresh = false;
              if (touchYDelta > 0) {
                e.preventDefault();
                return;
              }
            }
          }

          document.addEventListener('touchstart', touchstartHandler, false);
          document.addEventListener('touchmove', touchmoveHandler, false);      });
            //]]>    </script>

Solution 9 - Android

The best solution on pure CSS:

body {
	width: 100%;
	height: 100%;
	display: block;
	position: absolute;
	top: -1px;
	z-index: 1;
	margin: 0;
	padding: 0;
	overflow-y: hidden;
}
#pseudobody {
	width:100%;
	height: 100%;
	position: absolute;
	top:0;
	z-index: 2;
	margin: 0;
	padding:0;
	overflow-y: auto;
}

See this demo: https://jsbin.com/pokusideha/quiet

Solution 10 - Android

I find setting your body CSS overflow-y:hidden is the simplest way. If you did want to have a scrolling application page you can just use a div container with scrolling features.

Solution 11 - Android

Note that overflow-y is not inherited, so you need to set it on ALL block elements.

You can do this with jQuery simply by:

		$(document.body).css('overflow-y', 'hidden'); 
		$('*').filter(function(index) {
		    return $(this).css('display') == 'block';
		}).css('overflow-y', 'hidden');

Solution 12 - Android

Since a couple of weeks I found out that the javascript function that I used to disable the Chrome refresh action won't work anymore. I have made this to solve it:

$(window).scroll(function() {
   if ($(document).scrollTop() >= 1) {
      $("html").css({
         "touch-action": "auto"}
      );
   } else {
      $("html").css({
         "touch-action": "pan-down"
      });
   }
});

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

Solution 13 - Android

Pure js solution.

// Prevent pull refresh chrome
    var lastTouchY = 0;
    var preventPullToRefresh = false;
    window.document.body.addEventListener("touchstart", function(e){
        if (e.touches.length != 1) { return; }
        lastTouchY = e.touches[0].clientY;
        preventPullToRefresh = window.pageYOffset == 0;
    }, false);
 
    window.document.body.addEventListener("touchmove", function(e){
 
        var touchY = e.touches[0].clientY;
        var touchYDelta = touchY - lastTouchY;
        lastTouchY = touchY;
        if (preventPullToRefresh) {
            // To suppress pull-to-refresh it is sufficient to preventDefault the first overscrolling touchmove.
            preventPullToRefresh = false;
            if (touchYDelta > 0) {
                e.preventDefault();
                return;
            }
        }

    }, false);

Solution 14 - Android

What I did was add following to the touchstart and touchend/touchcancel events:

scrollTo(0, 1)

Since chrome does not scroll if it's not on scrollY position 0 this will prevent chrome from doing pull to refresh.

If it still does not work, try also doing that when the page loads.

Solution 15 - Android

I solved the pull-down-to-refresh problem with this:

html, body {
	width: 100%;
	height: 100%;
	overflow: hidden;
}

Solution 16 - Android

This worked for me:

html {
  overscroll-behavior-y: contain;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSebastianoView Question on Stackoverflow
Solution 1 - AndroidEvynView Answer on Stackoverflow
Solution 2 - AndroidMatthew MullinView Answer on Stackoverflow
Solution 3 - AndroidKevin SandowView Answer on Stackoverflow
Solution 4 - AndroidEclecticView Answer on Stackoverflow
Solution 5 - AndroidJosé LoguercioView Answer on Stackoverflow
Solution 6 - AndroidSarath AkView Answer on Stackoverflow
Solution 7 - AndroidnicbouView Answer on Stackoverflow
Solution 8 - AndroidSacky SanView Answer on Stackoverflow
Solution 9 - AndroidEugene FoxView Answer on Stackoverflow
Solution 10 - AndroidKeegan TeetaertView Answer on Stackoverflow
Solution 11 - AndroidkofifusView Answer on Stackoverflow
Solution 12 - AndroidWobboView Answer on Stackoverflow
Solution 13 - Androiduser1503606View Answer on Stackoverflow
Solution 14 - Androidxdevs23View Answer on Stackoverflow
Solution 15 - AndroidPaul IştoanView Answer on Stackoverflow
Solution 16 - AndroidMarc SolerView Answer on Stackoverflow