10

How to prevent pull-to-refresh in web applications for Chrome android?

I tried the answers from

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

body {
  overflow-y: hidden;
}

or

body {
  touch-action: none;
}

It did not work. any one has a solution that works? It is very bad for browsers to make pull-to-refresh default behavior, it is very undesirable for web applications.

Atef Hares
  • 4,715
  • 3
  • 29
  • 61
Dave
  • 759
  • 2
  • 9
  • 31

2 Answers2

1

Here's a CSS method of capturing the required touch gestures to prevent the pull to refresh:

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

This method seems to have worked well for other users. Hope it works for your needs.

Reference, plus there's more strategies discussed here: Disabling android's chrome pull-down-to-refresh feature

Chris J
  • 1,527
  • 14
  • 19
0

The code below is a javascript-only solution distilled from a couple of sources, which are towards the bottom.

If you are willing to change the structure of your pages, a CSS/HTML only option may work for you.

Additionally, the draft CSS property scroll-boundary-behavior is in the process of being standardized and added to Chrome provide this capability among a few others. As the implementation is very, very new, I provide links at the bottom of my answer.

Example

although jsfiddle's iframe structure prevents pull-to-refresh from working at all, I also tested the same script within a flat HTML document on Chrome Android 60.0.3112.116.

Full jsfiddle

event.preventDefault() can keep browser default behaviors such as pull-to-refresh from taking place. We want the usual browser behavior most of the time, just not when it would lead to a pull-to-refresh. Since a pull-to-refresh happens when touches are moving down the screen and we're scrolled to the top of the document, we'll only call preventDefault under those conditions.

//We're going to make a closure that will handle events
//so as to prevent the pull-to-refresh behavior.
var pullToRefreshPreventer = (function() {
  //To determine the direction in which a touch is moving,
  //we hold on to a map from touch identifier to touches
  //from the previous event.
  var previousTouches = {};
  return function(event) {
    //First we get all touches in this event and set up
    //the value which will replace `previousTouches`
    //before this event handler exits.
    var touches = Array.prototype.slice.call(event.touches);
    nextTouches = {}
    touches.forEach(function(touch){
      nextTouches[touch.identifier] = touch;
    });

    //Pull-to-refresh behavior only happens if we are
    //scrolled to the top of the document, so we can
    //exit early if we are somewhere in the middle.
    if(document.scrollingElement.scrollTop > 0) {
      previousTouches = nextTouches;
      return;
    }

    //Now we know that we are scrolled to the top of
    //the document;
    //look through the current set of touches and see
    //if any of them have moved down the page.
    for(var ix = 0; ix < touches.length; ix++) {
      var touch = touches[ix],
          id = touch.identifier;
      //If this touch was captured in a previous event
      //and it has moved downwards, we call preventDefault
      //to prevent the pull-to-refresh behavior.
      if(id in previousTouches && previousTouches[id].screenY < touch.screenY) {
        event.preventDefault();
        console.log("event.preventDefault() called")
        break;
      }
    }
    //lastly, we update previousTouches
    previousTouches = nextTouches;
  };
}());

//Since touch events which may call `preventDefault` can be
//much more expensive to handle, Chrome disallows such calls
//by default.  We must add the options argument `{passive: false}`
//here to make it work.
document.body.addEventListener('touchmove', pullToRefreshPreventer, {passive: false});
document.body.addEventListener('touchend', pullToRefreshPreventer, {passive: false});

References:

StackOverflow answer linking to chromestatus.com page

"Treat Document Level Touch Event Listeners as Passive", chromestatus

"Making touch scrolling fast by default"

"Touch events"

scroll-boundary-behavior links:

chromestatus

chromium bug

github issue proposing the standard

draft css module, last publish date 2017-09-07

ellisbben
  • 6,352
  • 26
  • 43
  • 1
    I haven't been able to test on tablet yet, but will accept as answer as I don't want you to miss out on bounty if it expires! Hope it works and will test and confirm asap! Thank you. – user1063287 Sep 25 '17 at 02:34
  • LMK how it goes! – ellisbben Sep 25 '17 at 12:57
  • Results: Remote debugging on PC, when using mouse to simulate swipe down, see orange information message in Chrome developer tools `Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted`. 1) On PC debugging screen, I **can** make page refresh using mouse to swipe down. 2) I **cannot** make page refresh using finger and swipe down on tablet. 3) However, I **can** make page refresh if I swipe down on a jqueryu ui autocomplete dropdown when it is at the top of its list. Because of 3, not successful yet. – user1063287 Oct 09 '17 at 02:41
  • PS This is how I am remote debugging from Android to PC https://stackoverflow.com/q/45560325 – user1063287 Oct 09 '17 at 02:47
  • Chrome version? – ellisbben Oct 09 '17 at 13:26
  • `Chrome 60.0.3112.116` on `Android 7.0.0; SM-P585Y Build/NRD90M` – user1063287 Oct 10 '17 at 00:12
  • Also, the jsfiddle does not exhibit pull to refresh behaviour with or without the JS code unfortunately. – user1063287 Oct 10 '17 at 00:31
  • 1
    Yeah, I noted that above-- "although jsfiddle's iframe structure prevents pull-to-refresh from working at all" -- the jsfiddle structures its pages so that there's always a DOM wrapper that prevents pull-to-refresh. If you go and try to directly visit the URL which it uses for the result iframe, it also throws it in a wrapper. – ellisbben Oct 10 '17 at 14:22
  • You can repro this if you take it off of jsfiddle. I just did the following: 1. on a desktop machine w/ chrome, clear browser cache; 2. visit https://jsfiddle.net/ca9mar2L/11/; 3. in the same browser tab, visit https://fiddle.jshell.net/ca9mar2L/11/show/; observe NO jsfiddle wrapper dom present; 4. download html file; email to myself, open in mobile chrome; observe pull-to-refresh does not work; 4. remove script tag containing example code; email to myself again; open in mobile chrome; observe pull-to-refresh works – ellisbben Oct 10 '17 at 14:25
  • Or, to avoid any chance of jsfiddle messing things up by adding a DOM wrapper, just copy this stuff to an html file that you directly control. – ellisbben Oct 10 '17 at 14:27
  • Sorry about that oversight re: jsfiddle environment. Thanks for detailed testing instructions. Results are still as described above however (the orange information message in console and jqueryui autocomplete swiping causes page refresh). – user1063287 Oct 10 '17 at 23:46