Elimine 300 ms de atraso em eventos de clique no Safari para celular

88

Eu li que o Safari para celular tem um atraso de 300 ms em eventos de clique desde o momento em que o link / botão é clicado até o momento em que o evento é disparado. O motivo do atraso é esperar para ver se o usuário pretende clicar duas vezes, mas de uma perspectiva de UX, esperar 300 ms geralmente é indesejável.

Uma solução para eliminar esse atraso de 300 ms é usar o manuseio de "tap" do jQuery Mobile. Infelizmente, não estou familiarizado com esta estrutura e não quero carregar uma estrutura grande se tudo que eu preciso é uma linha ou duas de código aplicado touchendda maneira certa.

Como muitos sites, meu site tem muitos eventos de clique como este:

$("button.submitBtn").on('click', function (e) {   
  $.ajaxSubmit({... //ajax form submisssion
});

$("a.ajax").on('click', function (e) {   
  $.ajax({... //ajax page loading
});

$("button.modal").on('click', function (e) {   
      //show/hide modal dialog
});

e o que eu gostaria de fazer é me livrar do atraso de 300 ms em TODOS os eventos de clique usando um único snippet de código como este:

$("a, button").on('tap', function (e) {
 $(this).trigger('click');
 e.preventDefault();
});

Isso é uma ideia boa / ruim?

Tim Peterson
fonte
@Pointy obrigado, isso pode funcionar ...
tim peterson
"... obviamente isso não é ótimo do ponto de vista da experiência do usuário." Eu ficaria desconfiado com essa suposição.
Oliver Moran,
1
@OliverMoran, obrigado pela correção, acabei de editar essa frase, veja a pergunta acima ..
tim peterson
1
pode ser uma solução: stackoverflow.com/a/12969739/1491212
Armel Larcier

Respostas:

72

Agora, alguns navegadores móveis eliminam 300 ms de atraso de clique se você definir a janela de visualização. Você não precisa mais usar soluções alternativas.

<meta name="viewport" content="width=device-width, user-scalable=no">

Atualmente é compatível com Chrome para Android , Firefox para Android e Safari para iOS

No entanto, no iOS Safari , o toque duplo é um gesto de rolagem em páginas que não podem ser alteradas. Por esse motivo, eles não podem remover o atraso de 300ms . Se eles não puderem remover o atraso nas páginas que podem ser ampliadas, é improvável que o removam nas páginas que podem ser ampliadas.

Telefones Windows também retêm o atraso de 300 ms em páginas não zeráveis, mas eles não têm um gesto alternativo como o iOS, então é possível para eles removerem esse atraso como o Chrome fez.Você pode remover o atraso no Windows Phone usando:

html {
-ms-touch-action: manipulation;
touch-action: manipulation;
}

Fonte: http://updates.html5rocks.com/2013/12/300ms-tap-delay-gone-away

ATUALIZAÇÃO dezembro de 2015

Até agora, o WebKit e o Safari no iOS tinham um atraso de 350 ms antes que toques simples ativassem links ou botões para permitir que as pessoas ampliassem as páginas com um toque duplo. O Chrome mudou isso há alguns meses, usando um algoritmo mais inteligente para detectar isso e o WebKit seguirá com uma abordagem semelhante . O artigo oferece alguns insights excelentes sobre como os navegadores funcionam com gestos de toque e como os navegadores ainda podem ficar muito mais inteligentes do que são hoje.

ATUALIZAÇÃO março de 2016

No Safari para iOS, o tempo de espera de 350 ms para detectar um segundo toque foi removido para criar uma resposta de “toque rápido”. Isso é habilitado para páginas que declaram uma janela de visualização com width = device-width ou user-scalable = no. Os autores também podem optar pelo comportamento do toque rápido em elementos específicos usando o CSS touch-action: manipulationconforme documentado aqui (role para baixo até o título 'Comportamento do toque rápido do estilo') e aqui .

NoNameProvided
fonte
Na verdade, existem eventos de toque para WP: stackoverflow.com/questions/13396297/…
Cedric Reichenbach
1
Sim, que é controlado por -ms-touch-action, como escrevi em meu post.
NoName Fornecido em
1
O problema ainda existe no iOS ao executar o aplicativo a partir da página inicial (autônomo). Se alguém encontrou uma solução decente para isso, seria muito bem-vindo
Miguel Guardo
1
Definir os valores de ação de toque para manipulação é o que mais se aproxima do desempenho desejado, mas, como @MiguelGuardo afirmou anteriormente, o efeito é perdido quando você adiciona a página da web à sua tela inicial no iOS como um aplicativo independente.
T. Steele,
Pode ser que alguém possa hackear o UIWebView - stackoverflow.com/questions/55155560/…
Eazy
43

Este plugin -FastClick desenvolvido pelo Financial Times faz isso perfeitamente para você!

Certifique-se de adicionar event.stopPropagation();e / ou event.preventDefault();diretamente após a função de clique, caso contrário, ela pode ser executada duas vezes da mesma forma que para mim, ou seja:

$("#buttonId").on('click',function(event){
    event.stopPropagation(); event.preventDefault();
   //do your magic

});
Jonathan
fonte
3
Isso não é perfeito se você estiver usando jquery mobile (versão 1.3.2 de qualquer maneira), ele interrompe o fechamento do widget do painel github.com/jquery/jquery-mobile/issues/6440
ryan0
que event.stopPropagation () é exatamente o motivo pelo qual vim para esta página em primeiro lugar. Funciona perfeitamente, obrigado!
Lukas de
@Jonathan fastclickfaz sua mágica apenas em dispositivos de toque que têm um problema de atraso (caso contrário, ele é automaticamente ignorado ). No entanto, simplesmente adicionar stopPropagation& preventDefaultapós cada evento de clique pode ter um efeito colateral indesejado em todos os manipuladores de eventos em todos os navegadores.
usuário
17

Eu sei que isso é antigo, mas você não pode simplesmente testar para ver se "touch" é compatível com o navegador? Em seguida, crie uma variável que seja "touchend" ou "click" e use essa variável como o evento que é vinculado ao seu elemento.

var clickOrTouch = (('ontouchend' in window)) ? 'touchend' : 'click';
$('#element').on(clickOrTouch, function() {
    // do something
});

Portanto, esse exemplo de código verifica se o evento "touchend" é compatível com o navegador e, caso contrário, usamos o evento "click".

(Editar: mudou "touchend" para "ontouchend")

Michael Turnwall
fonte
Obrigado, gosto da simplicidade disso. Eu adoraria que outras pessoas falassem sobre suas possíveis armadilhas.
tim Peterson
4
O evento touchend não pode substituir o evento de clique devido ao que @Andreas Köberle explica em sua resposta. Por exemplo, se você rolar e seu dedo parar em um botão, isso irá acionar o link ...
adriendenat
Esta é uma solução incrível e leve, sem a necessidade de incluir uma grande biblioteca adicional de coisas apenas para se livrar do atraso. Gostaria de poder votar 5 vezes neste! (contanto que você não esteja preocupado com o cenário de rolagem!)
tonejac
2
Para computadores / navegadores com suporte a eventos de toque e clique, essa é uma solução ruim.
Alexander O'Mara,
1
Alguns dispositivos suportam eventos de toque e clique, e você deseja que ambos disparem em situações diferentes. Experimentamos situações em que algumas janelas ou tablets Android que têm um mouse obviamente disparam um evento de clique quando você clica em algo, mas também disparam eventos de toque quando você toca. Isso significava que você tinha que ouvir os dois eventos. Como um fator complicador adicional, alguns dispositivos sintetizariam um evento de clique quando você tocasse, portanto, ouvir ambos nesses dispositivos poderia resultar no acionamento do manipulador de eventos duas vezes, a menos que você fosse cuidadoso.
1800 INFORMAÇÕES
12

Encontrei uma alternativa extremamente popular chamada Hammer.js (página do Github) que acho que é a melhor abordagem.

Hammer.js é uma biblioteca de toque com mais recursos (tem muitos comandos de deslizamento) do que Fastclick.js (resposta mais votada).

Porém, tenha cuidado: a rolagem rápida em dispositivos móveis tende a realmente travar a IU quando você usa o Hammer.js ou Fastclick.js. Esse é um grande problema se o seu site tiver um feed de notícias ou uma interface em que os usuários rolarão muito (pareceria a maioria dos aplicativos da web). Por esse motivo, não estou usando nenhum desses plug-ins no momento.

Tim Peterson
fonte
2
Não use Hammerjs. Tem alguns problemas sérios, por exemplo: github.com/EightMedia/hammer.js/issues/388 github.com/EightMedia/hammer.js/issues/366 Pelo menos não use até que sejam consertados
Adonis K. Kakoulidis
2

De alguma forma, desativar o zoom parece desativar esse pequeno atraso. Faz sentido, já que o toque duplo não é mais necessário.

Como posso "desativar" o zoom em uma página da web para celular?

Mas esteja ciente do impacto na usabilidade que isso terá. Pode ser útil para páginas da web projetadas como aplicativos, mas não deve ser usado para páginas 'estáticas' de uso mais geral IMHO. Eu o uso para um projeto de estimação que precisa de baixa latência.

Ayke
fonte
1

Infelizmente, não existe uma maneira fácil de fazer isso. Portanto, apenas usar touchstartou touchenddeixará você com outros problemas, como alguém que começa a rolar ao clicar em um botão, por exemplo. Usamos o zepto por um tempo e, mesmo com esse framework muito bom, alguns problemas surgiram com o tempo. Muitos deles estão fechados , mas parece que não é um campo de solução simples.

Temos esta solução para lidar globalmente com cliques em links:

   $(document.body).
    on('tap', 'a',function (e) {
      var href = this.getAttribute('href');
      if (e.defaultPrevented || !href) { return; }
      e.preventDefault();
      location.href= href;
    }).
    on('click', 'a', function (e) {
      e.preventDefault();
    });
Andreas Köberle
fonte
- @ Andreas, obrigado, então você advogaria não usar a tapsolução geral que postei e fatorar os tapeventos elemento por elemento?
tim Peterson,
Não, esse não é o ponto. A questão não é tentar construir uma tapsolução sozinho. Vou atualizar minha resposta.
Andreas Köberle
- @ Andreas, obrigado pela sua resposta, muitos eventos de clique são AJAX, então o evento padrão já foi evitado. Você se importaria de atualizar sua resposta para lidar com isso também? Parece que minha tapsolução geral seria igual à sua neste caso?
tim Peterson,
1

Procurei uma maneira fácil sem jquery e sem biblioteca fastclick. Isso funciona para mim:

var keyboard = document.getElementById("keyboard");
var buttons = keyboard.children;
var isTouch = ("ontouchstart" in window);
for (var i=0;i<buttons.length;i++) {
    if ( isTouch ) {
        buttons[i].addEventListener('touchstart', clickHandler, false);         
    } else {
        buttons[i].addEventListener('click', clickHandler, false);          
    }
}
Jarda Pavlíček
fonte
0

Apenas para fornecer algumas informações extras.

No iOS 10, os <button>s na minha página não podiam ser acionados continuamente. Sempre havia um atraso.

Tentei fastclick / Hammer / tapjs / substituindo click por touchstart, todos falharam.

ATUALIZAÇÃO: a razão parece ser que o botão está muito próximo da borda! mova-o para perto do centro e o atraso se foi!

theaws.blog
fonte
0

Você deve declarar explicitamente o modo passivo :

window.addEventListener('touchstart', (e) => {

    alert('fast touch');

}, { passive : true});
Bruno Andrade
fonte
0

No jQuery você pode vincular o evento "touchend", que dispara o código imediatamente após o toque (é como uma tecla no teclado). Testado nas versões atuais dos tablets Chrome e Firefox. Não se esqueça do "clique" também para seus laptops com tela de toque e dispositivos de desktop.

jQuery('.yourElements').bind('click touchend',function(event){

    event.stopPropagation();
    event.preventDefault();

	// everything else
});

Benjamin
fonte