Desativando o recurso pull-down-to-refresh do chrome

135

Eu criei um pequeno aplicativo Web HTML5 para minha empresa.

Este aplicativo exibe uma lista de itens e tudo funciona bem.

O aplicativo é usado principalmente em telefones Android e Chrome como navegador. Além disso, o site é salvo na tela inicial para que o Android gerencie tudo como um aplicativo ( usando um WebView, eu acho ).

O Chrome Beta (e acho que também o Android System WebView) introduziu o recurso "puxar para atualizar" ( veja este link, por exemplo ).

Esse é um recurso útil, mas eu queria saber se ele pode ser desativado com alguma metatag (ou material javascript) porque a atualização pode ser facilmente acionada pelo usuário enquanto você navega na lista e o aplicativo inteiro é recarregado.

Além disso, esse é um recurso não necessário ao aplicativo.

Sei que esse recurso ainda está disponível apenas no Chrome beta, mas tenho a sensação de que ele também está chegando ao aplicativo estável.

Obrigado!

Editar: desinstalei o Chrome Beta e o link fixado na tela inicial agora é aberto com o Chrome estável. Portanto, os links fixados começam no Chrome e não em uma visualização na web.

Edit: today (19/03/2015) o pull-down-to-refresh chegou ao chrome estável.

Edit: from @Evyn answer Eu sigo este link e recebi este código javascript / jquery que funciona.

Como o @bcintegrity apontou, espero uma solução de manifesto de site (e / ou uma meta tag) no futuro.

Além disso, sugestões para o código acima são bem-vindas.

Sebastiano
fonte
18
Sim, isso é realmente uma merda. Estava no meio da forma e rolou para o topo muito longe e atualizou e perdeu tudo. Esse é um recurso padrão retardado. Clico no ícone Atualizar se quiser atualizar!
Brock Hensley
12
Seria bom se esse recurso pudesse ser desativado no manifesto do meu aplicativo da web. Infelizmente, todas as páginas do meu aplicativo da Web têm conteúdo rolável, tornando quase impossível navegar sem atualizar. Estou um pouco irritado. : /
bcintegrity 27/03
Esta é uma boa informação. Eu odeio sites que desativam o recurso de atualização. Gostaria de desenvolver um recurso para fazer a atualização funcionar, independentemente do conteúdo da página.
244
5
Falando como desenvolvedor da Web, a atualização suspensa é incompatível com sites controlados por dados, pois recarrega o aplicativo. Torna impossível para um usuário rolar para o topo de uma página sem atualizar a página inteira. Sou 100% favorável ao Chrome, espero que eles removam esse anti-recurso.
Feijão
3
Enfrentei-me com esse "recurso" do GC ... muitas páginas da web, muitos formulários, um puxão descuidado com o dedo e os dados desta página são perdidos! Função de retardo IMO. Por padrão, ele deve estar desativado; quem precisar, deixe o código. Eu me perguntava por dias porque meus "clientes" de vez em quando perdeu seus dados em GC ...
Ludus H

Respostas:

157

A ação padrão do efeito de puxar para atualizar pode ser efetivamente evitada seguindo um destes procedimentos:

  1. preventDefaultuma parte da sequência de toque, incluindo qualquer um dos seguintes (na ordem de mais perturbador para menos perturbador):
    • uma. Todo o fluxo de toque (não é o ideal).
    • b. Todos os toques de rolagem superiores.
    • c. O primeiro touchmove de rolagem superior.
    • d. O primeiro touchmove de rolagem superior apenas quando 1) o touchstart inicial ocorreu quando o deslocamento da rolagem da página e era zero e 2) o touchmove induzia a rolagem superior.
  2. Aplicando " touch-action: none" a elementos segmentados por toque, quando apropriado, desativando ações padrão (incluindo puxar para atualizar) da sequência de toque.
  3. Aplicando “ overflow-y: hidden” ao elemento body, usando uma div para o conteúdo rolável, se necessário.
  4. Desativando o efeito localmente via chrome://flags/#disable-pull-to-refresh-effect).

Ver mais

Evyn
fonte
12
Obrigado @Evyn. Não acredito que essa foi uma solução rápida! Defino o CSS para o elemento body como overflow-y: hidden.
bcintegrity
Obrigado @Evyn, decidi usar o método preventDefault () (primeiro ponto). Em particular, achei interessante o código fonte do link ( link ) que está dentro do documento que você vinculou. Vou colocar o código no final da minha pergunta.
Sebastiano
2
como é que "chrome: // flags (desativar-puxar-para-atualizar-efeito)" é feito programaticamente?
Alguém em algum lugar
2
@SomeoneSomewhere tanto quanto eu sei, não é impossível. configurações do Chrome só podem ser definidos pelo usuário - seria um risco de segurança de outra forma
Mike
1
Aplicar "overflow-y: hidden" ao elemento body, usando uma div para o conteúdo rolável, funcionou para mim.
Robin
103

Solução simples para 2019+

O Chrome 63 adicionou uma propriedade css para ajudar exatamente com isso. Leia este guia do Google para ter uma boa idéia de como lidar com isso.

Aqui está o TL: DR

A propriedade CSS overscroll-behavior permite que os desenvolvedores substituam o comportamento de rolagem de estouro padrão do navegador ao atingir a parte superior / inferior do conteúdo. Os casos de uso incluem desativar o recurso de puxar para atualizar no celular, remover os efeitos de brilho excessivo e faixas de borracha e impedir que o conteúdo da página role enquanto estiver sob uma sobreposição / modal.

Para que funcione, tudo o que você precisa adicionar é o seguinte no seu CSS:

body {
  overscroll-behavior: contain;
}

Por enquanto, ele também é suportado apenas pelo Chrome, Edge e Firefox, mas tenho certeza de que o Safari o adicionará assim que parecerem totalmente integrados aos profissionais de serviço e ao futuro dos PWAs.

Matthew Mullin
fonte
16
este é o mais atualizado anwser
colxi
Alguém mais percebeu isso atrasando a aparência do banner cromado "Adicionar à tela inicial"?
Nathaniel Hill
2
teve de aplicar-lo para o elemento html, a fim de desativá-lo
Jakob Eriksson
1
Este trabalho apenas para Android, Chrome no iOS ainda mostram brilha efeito + puxar nativa para refrescar
Fky
developers.google.com/web/updates/2017/11/overscroll-behavior este link também pode explicar melhor
arung86
15

No momento, você só pode desativar esse recurso via chrome: // flags / # disable-pull-to-refresh-effect - abra diretamente do seu dispositivo.

Você pode tentar capturar touchmoveeventos, mas as chances são muito pequenas de obter um resultado aceitável.

Kevin Sandow
fonte
1
Obrigado Kevin pela resposta. Não pude aceitar isso como o aceito porque estava procurando por algo que não dependesse dos usuários. No entanto, esse método funciona, então eu votarei sua resposta à medida que eu ganhar reputação suficiente.
Sebastiano
É uma pena que essa seja a melhor solução - e se essa configuração de sinalizador mudar no futuro, ou você quiser usar o pull para atualizar em outros sites ... Surpreendido que isso não pode ser controlado programaticamente. :(.
user1063287
1
Se o CEO do Google estiver lendo isso, remova esse recurso. Não parece que ele possa ser desabilitado de maneira fácil e abrangente e, na minha opinião, é um "recurso" de aplicativo da web que nem todos os aplicativos da web desejam.
user1063287
8

Eu uso o MooTools e criei uma classe para desativar a atualização em um elemento de destino, mas o ponto crucial é (JS nativo):

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;
});

Tudo o que fazemos aqui é encontrar a direção y do touchmove e, se estivermos movendo a tela para baixo e o scroll de destino for 0, interromperemos o evento. Assim, nenhuma atualização.

Isso significa que estamos disparando a cada 'movimento', o que pode ser caro, mas é a melhor solução que encontrei até agora ...

Eclético
fonte
2
É seguro parar de assistir assim que o usuário rolar para baixo, pois a atração para atualizar não tem chance de ser acionada. Da mesma forma, se scrolltop > 0o evento não for acionado. Na minha implementação, vinculo o touchmoveevento touchstartsomente se scrollTop <= 0. Desvinculo-o assim que o usuário rola para baixo ( initialY >= e.touches[0].clientY). Se o usuário rolar para cima ( previousY < e.touches[0].clientY), eu ligo preventDefault.
Nicolas Bouliane 7/08
7

Depois de muitas horas tentando, esta solução funciona para mim

$("html").css({
    "touch-action": "pan-down"
});
José Loguercio
fonte
Não é necessário usar o jQuery para isso. Basta adicionar o "touch-action: pan-down" ao seu arquivo de código css.
Keegan Teetaert 12/02
1
apenas usar isso em seu css:html{touch-action:pan-down}
csandreas1
5

AngularJS

Eu o desativei com sucesso com esta diretiva AngularJS:

//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);
        }
    };
});

É seguro parar de assistir assim que o usuário rolar para baixo, pois a atração para atualizar não tem chance de ser acionada.

Da mesma forma, se scrolltop > 0o evento não for acionado. Na minha implementação, vinculo o evento touchmove no touchstart, apenas se scrollTop <= 0. Desvinculo assim que o usuário rola para baixo ( initialY >= e.touches[0].clientY). Se o usuário rolar para cima ( previousY < e.touches[0].clientY), eu ligo preventDefault().

Isso nos impede de assistir a eventos de rolagem desnecessariamente, mas bloqueia o excesso de rolagem.

jQuery

Se você estiver usando jQuery, este é o equivalente não testado . elementé um elemento jQuery:

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);

Naturalmente, o mesmo também pode ser alcançado com JS puro.

Nicolas Bouliane
fonte
Eu tive que usar e.originalEvent.touches[0].clientYpara fazer isso funcionar. Ainda lutando com a lógica quando impedir a recarga. No momento, parece impedir aleatoriamente a rolagem para cima ... Coloque agradecimentos pela dica na direção certa!
Thomas
A que elemento a diretiva foi adicionada?
Fizzix
A diretiva AngularJS funcionou bem para mim. Estamos usando o Framework7 e o Angular juntos (não sugeriria isso) e algo estava bloqueando o comportamento correto do scrollTop. Então eu tive que modificar quando o blockScroll seria adicionado ao elemento. Mas só queria dizer obrigado!
Dustin Jensen
5

Javascript simples

Eu implementei usando javascript padrão. Simples e fácil de implementar. Basta colar e ele funciona bem.

<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>
Sacky San
fonte
Isso funcionou muito bem no Chrome, mas causou alguns problemas ao rolar para cima no Safari / iOS 9.2 ao rolar uma div na parte superior da página (reproduzida aqui ). Acabei usando o body: {overflow-y:hidden}truque de Evyn acima .
precisa saber é o seguinte
downvoting como não funciona no Chrome, mas funciona no Firefox exemplo, embed.plnkr.co/rqbOVSOkKyhAgoHK2sEP abrir em MOBILE SOMENTE
Vijay
o link que você colou também funcionou no chrome no meu celular Android. não permitiu atualizar ao puxar. qual versão do android / ios você está usando para testar?
Sacky San
2
No Chrome 56, você precisa definir passive: false para fazê-lo funcionar: document.addEventListener ('touchstart', touchstartHandler, {passive: false}); document.addEventListener ('touchmove', touchmoveHandler, {passivo: false});
Anton Kuzmin 27/10
4

A melhor solução em CSS puro:

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;
}

Veja esta demonstração: https://jsbin.com/pokusideha/quiet

Eugene Fox
fonte
você realmente testou isso em um celular usando o Chrome?
Gregor Voinov
3

Acho que definir o seu corpo CSS overflow-y: hidden é a maneira mais simples. Se você deseja ter uma página de aplicativo de rolagem, basta usar um contêiner div com recursos de rolagem.

Keegan Teetaert
fonte
Parece impossível corrigi-lo com uma linha, mas funciona como um encanto, muito obrigado!
Ignacio Ara
2

Observe que o overflow-y não é herdado, portanto, você deve configurá-lo em TODOS os elementos do bloco.

Você pode fazer isso com o jQuery simplesmente:

        $(document.body).css('overflow-y', 'hidden'); 
        $('*').filter(function(index) {
            return $(this).css('display') == 'block';
        }).css('overflow-y', 'hidden');
kofifus
fonte
Entre as outras respostas, essa realmente ajuda. Obrigado. Adicione esta linha ao css. corpo {estouro-y: oculto; }
Sarin JS
2

Há algumas semanas, descobri que a função javascript usada para desativar a ação de atualização do Chrome não funcionará mais. Eu fiz isso para resolvê-lo:

$(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>

Wobbo
fonte
1

Solução js pura.

// 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);
user1503606
fonte
0

O que eu fiz foi adicionar o seguinte aos touchstarte touchend/ touchcancelevents:

scrollTo(0, 1)

Como o chrome não rola se não estiver na posição scrollY, 0isso impedirá o chrome de puxar para atualizar.

Se ainda assim não funcionar, tente fazer isso quando a página for carregada.

xdevs23
fonte
0

Resolvi o problema de puxar para baixo para atualizar com isso:

html, body {
    width: 100%;
    height: 100%;
    overflow: hidden;
}
Paul Iştoan
fonte