Como vincular os eventos 'touchstart' e 'click', mas não responder a ambos?


198

Estou trabalhando em um site móvel que precisa funcionar em uma variedade de dispositivos. Os que estão me dando dor de cabeça no momento são BlackBerry.

Precisamos oferecer suporte a cliques no teclado e a eventos de toque.

Idealmente, eu apenas usaria:

$thing.click(function(){...})

mas o problema que estamos enfrentando é que alguns desses dispositivos blackberry têm um atraso muito irritante desde o momento do toque até o acionamento de um clique.

O remédio é usar o touchstart:

$thing.bind('touchstart', function(event){...})

Mas como faço para vincular os dois eventos, mas apenas disparar um? Ainda preciso do evento de clique para dispositivos de teclado, mas é claro que não quero que o evento de clique seja acionado se estiver usando um dispositivo de toque.

Uma pergunta bônus: existe alguma maneira de fazer isso e acomodar adicionalmente navegadores que nem sequer têm um evento de toque inicial? Ao pesquisar isso, parece que o BlackBerry OS5 não oferece suporte ao touchstart e também precisará contar com os eventos de clique desse navegador.

TERMO ADITIVO:

Talvez uma pergunta mais abrangente seja:

Com o jQuery, é possível / recomendado lidar com as interações de toque e de mouse com as mesmas ligações?

Idealmente, a resposta é sim. Caso contrário, tenho algumas opções:

1) Usamos o WURFL para obter informações sobre o dispositivo, para criar nossa própria matriz de dispositivos. Dependendo do dispositivo, usaremos o touchstart OU clique.

2) Detectar para suporte por toque no navegador via JS (preciso fazer mais pesquisas sobre isso, mas parece que isso é possível).

No entanto, isso ainda deixa um problema: e os dispositivos que suportam AMBOS. Alguns dos telefones suportados (nomeadamente o Nokias e BlackBerries) têm ecrãs tácteis e teclados. Então isso me leva de volta à pergunta original ... existe uma maneira de permitir as duas coisas de uma vez?


2
É melhor ligar para iniciar e tocar e escrever sua própria lógica de clique ao lado de sua lógica de toque. O retorno de chamada de clique embutido, sem conhecimento de toques.
perfil completo de Justin808

Não tenho certeza se sigo, Justin. Ainda não teria um evento touchstart e click vinculado a ele?
DA.

@ DA - não, você não ligaria ao retorno de chamada .click (). Vou tentar escrever uma resposta em algum código sudo. Eu não tenho um dispositivo de toque útil para escrever código verdadeiro :)
Justin808

Ah, mas para esclarecer, ainda preciso de eventos de cliques, pois haverá pessoas acessando este site com dispositivos não sensíveis ao toque.
DA.

2
O uso .bind('touchstart mouseup')o resolverá (com base em um dos comentários abaixo)
oriadam 16/05

Respostas:


135

Atualização : Confira o projeto PolyFill de eventos do ponteiro do jQuery , que permite vincular a eventos de "ponteiro" em vez de escolher entre mouse e toque.


Associe-se a ambos, mas faça um sinalizador para que a função seja acionada apenas uma vez a cada 100ms.

var flag = false;
$thing.bind('touchstart click', function(){
  if (!flag) {
    flag = true;
    setTimeout(function(){ flag = false; }, 100);
    // do something
  }

  return false
});

7
hmm ... parece hacky, mas isso poderia funcionar. O problema é que, em alguns desses dispositivos, é um atraso perceptível ... talvez quase um segundo ... o que seria irritante para outros em um dispositivo mais rápido.
DA.

15
Em vez de usar clickalterá-lo para mousedown... talvez o longo atraso seja a diferença entre mousedowne mouseupqual é a forma como a clické determinada.
Mottie

7
Esta é a solução que eu usei. Obrigado! O que faço é sinalizar um item touchendaplicando uma classe de touched. Isso é acionado antes que o evento click seja chamado. Em seguida, adiciono um evento de clique que primeiro verifica a existência dessa classe. Se estiver lá, assumimos que os eventos de toque foram acionados e não fazemos nada com o clique, exceto remover o nome da classe para 'redefini-lo' para a próxima interação. Se a classe não estiver lá, assumimos que eles usaram um teclado para clicar no item e acionar a função a partir daí.
DA.

7
Você deve estar ciente de que a maioria dos navegadores de telefone possui um atraso de 300 ms no evento de clique. Portanto, você deve aumentar o deleay para pelo menos 300. Consulte este artigo sobre a questão do atraso de 300 ms . Isso é basicamente para ativar a funcionalidade "clique duplo para aumentar o zoom", que era um recurso muito importante nos primeiros dias do iPhone, quando a maioria dos sites foi projetada para exibição em uma grande tela da área de trabalho.
temor

12
Esta questão foi vista quase 100k vezes neste momento. Esse parece ser um caso / problema de uso extraordinariamente comum. No entanto, essa resposta e outras questões sobre essa e outras questões semelhantes parecem hacky. A solução do Google, embora mais bem pensada do que a maioria, parece ser apenas um hack mais robusto. Para algo tão simples como um manipulador de cliques, parece que a solução deve ser simples. Ninguém encontrou uma solução sem hackers para esse problema?
Jinglesthula

73

Essa é a correção que eu "crio" e remove o GhostClick e implementa o FastClick. Experimente por conta própria e deixe-nos saber se funcionou para você.

$(document).on('touchstart click', '.myBtn', function(event){
        if(event.handled === false) return
        event.stopPropagation();
        event.preventDefault();
        event.handled = true;

        // Do your magic here

});

2
helgatheviking: jsbin.com/ijizat/25 No JavaScript, há uma função chamada TouchClick que incorpora o acima.
quer

5
Eu ALTAMENTE prefiro este método de todas as respostas para esta pergunta, porque é a) não testar os recursos do dispositivo eb) não usar setTimeout. Na minha experiência, soluções para problemas como esses que usam setTimeout podem funcionar na maioria dos casos, mas o tempo dos eventos é bastante arbitrário e específico do dispositivo.
precisa

7
Isso não funcionará porque, ao passar 'event' na função anônima, ele se tornará um objeto local nessa função; portanto, event.handledserá igual undefinedsempre que o evento for disparado. Aqui está uma solução: defina o sinalizador 'manipulado' no próprio elemento de destino usando jQuery.data().
thdoan

3
Você tem event.stopPropagation();e, event.preventDefault();pelo meu entendimento (e teste), o evento só pode ser disparado uma vez com isso. então por que verificar if(event.handled !== true)? Para mim, não é necessário ou sinto falta de algo?
Nbar 4/16

2
Não deve event.handledser verificado o valor true? Tanto quanto eu posso dizer, nunca false. Começa como undefinede, em seguida, você deseja saber se foi definido true.
ACJ

49

Você pode tentar algo como isto:

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);

5
Eu acho que o problema é testar os recursos do dispositivo e não o que o usuário está fazendo, certo? O desafio é que precisamos oferecer suporte a dispositivos de toque que também possuem teclados (para que eles possam usar os dois)
DA.

8
Essa não é uma boa ideia - interromperá o evento para um usuário com uma tela sensível ao toque e um mouse ao usá-lo (laptops com telas sensíveis ao toque estão se tornando bastante comuns).
Kristian J.

1
Artigo excelente relacionado a isso: hacks.mozilla.org/2013/04/…
Luke Melia

O Windows 8 sempre conectará o evento 'touchstart' se for usado porque 'ontouchstart' retornará 'true' para este sistema operacional. Portanto, provavelmente não é a melhor resposta, a menos que seu código esteja sendo executado apenas em dispositivos touchscreen verdadeiros, mas se for esse o caso, você não precisará da verificação.
Neil Monroe

Em laptops com tela sensível ao toque, o usuário não poderá clicar em nada usando esse código no chrome (pelo menos, chrome 46). IE 11 parece funcionar embora.
David Sherret 27/10/2015

42

Geralmente isso também funciona:

$('#buttonId').on('touchstart click', function(e){
    e.stopPropagation(); e.preventDefault();
    //your code here

});

8
@ Jonathan errado, se você estiver usando versões mais recentes do jQuery. O Live foi descontinuado na versão 1.7.
Mike Barwick #

Eu também tentei (e li em outro lugar - não foi minha ideia) com e.stopPropagation (); e.preventDefault (); e funciona (para mim) quase, mas descobri mais tarde que, na verdade, funciona como previsto 20 vezes, mas uma vez não, por isso não é à prova de balas. Olhe aqui: stackoverflow.com/questions/25671053/… Aprecie seus comentários sobre esse assunto!
Garavani

@ MikeBarwick Você poderia explicar o seu comentário ou fornecer um link que o explique? Agradeço antecipadamente.
Billal Begueradj 9/09/16

22

Apenas adicionar return false;no final da on("click touchstart")função de evento pode resolver esse problema.

$(this).on("click touchstart", function() {
  // Do things
  return false;
});

Na documentação do jQuery em .on ()

Retornar falsede um manipulador de eventos chama automaticamente event.stopPropagation()e event.preventDefault(). Um falsevalor também pode ser passado para o manipulador como uma abreviação de function(){ return false; }.


2
funciona muito bem, incêndios apenas uma vez em dispositivos com ambos - esta parece ser a solução menos hacky mais limpo para mim
Adam Cooper

Funcionou melhor para mim e realmente fazia sentido o porquê.
Amir5000

Alguma desvantagem para isso? Por que uma solução tão simples não é amplamente conhecida?
ESR

10

Eu tive que fazer algo semelhante. Aqui está uma versão simplificada do que funcionou para mim. Se um evento de toque for detectado, remova a ligação de clique.

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click');

  //your code here
});

No meu caso, o evento click foi vinculado a um <a>elemento, então tive que remover a ligação de clique e religar um evento de clique que impedia a ação padrão do <a>elemento.

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click').on('click', function(e){ e.preventDefault(); });

  //your code here
});

Eu gostaria que isso funcionasse como está, mas não funciona. $ (this) não aponta para o que você pensa que faz.
DaveSimplere1

8

Eu consegui da seguinte maneira.

Mole-mole...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});

4
isso não dispara duas vezes em dispositivos que registram um clique e um touchstart?
DA.

8
Desculpe, eu te conheci. Sim, você está certo. Um toque gerará um evento touchstart e também um clique. Isso é chamado de clique fantasma. O Google implementou uma solução para isso. Pode não ser simples, mas funciona perfeitamente. Aqui está o link. code.google.com/mobile/articles/fast_buttons.html#ghost
Hasanavi

2
pode ser trivial, mas você está perdendo o eparâmetrofunction()
ProblemsOfSumit

@Hasanavi esta foi uma grande correção no entanto, achamos melhores resultados usando .no
Neil

1
Obrigado @Neil. Sim, usar .on é uma ideia melhor, pois pode ser removido em versões futuras. Eu mudei meu exemplo de bind para on.
Hasanavi

5

Acredito que a melhor prática agora é usar:

$('#object').on('touchend mouseup', function () { });

toque

O evento de toque final é acionado quando um ponto de toque é removido da superfície de toque.

O evento touchend não acionará nenhum evento do mouse.


mouseup

O evento mouseup é enviado para um elemento quando o ponteiro do mouse está sobre o elemento e o botão do mouse é liberado. Qualquer elemento HTML pode receber este evento.

O evento de mouseup não acionará nenhum evento de toque.

EXEMPLO

$('#click').on('mouseup', function () { alert('Event detected'); });
$('#touch').on('touchend', function () { alert('Event detected'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="click">Click me</h1>
<h1 id="touch">Touch me</h1>


EDIT (2017)

A partir de 2017, navegadores começando com o Chrome e dando passos para tornar o evento de clique .on("click")mais compatível para mouse e toque, eliminando o atraso gerado por eventos de toque nas solicitações de clique.

Isso leva à conclusão de que voltar a usar apenas o evento click seria a solução mais simples para o futuro.

Ainda não fiz nenhum teste entre navegadores para ver se isso é prático.


Isso parecia promissor para mim, então eu brinquei com isso um monte, mas no final das contas eu achei mouseupresultados imprevisíveis, especialmente ao usar um trackpad em vez de um mouse tradicional.
skybondsor

4

Geralmente, você não deseja misturar a API de toque padrão e sem toque (clique). Depois de entrar no mundo do toque, é mais fácil lidar apenas com as funções relacionadas ao toque. Abaixo está um pseudo-código que faria o que você deseja.

Se você se conectar no evento touchmove e rastrear os locais, poderá adicionar mais itens na função doTouchLogic para detectar gestos e outros enfeites.

var touchStartTime;
var touchStartLocation;
var touchEndTime;
var touchEndLocation;

$thing.bind('touchstart'), function() {
     var d = new Date();
     touchStartTime = d.getTime();
     touchStartLocation = mouse.location(x,y);
});

$thing.bind('touchend'), function() {
     var d = new Date();
     touchEndTime= d.getTime();
     touchEndLocation= mouse.location(x,y);
     doTouchLogic();
});

function doTouchLogic() {
     var distance = touchEndLocation - touchStartLocation;
     var duration = touchEndTime - touchStartTime;

     if (duration <= 100ms && distance <= 10px) {
          // Person tapped their finger (do click/tap stuff here)
     }
     if (duration > 100ms && distance <= 10px) {
          // Person pressed their finger (not a quick tap)
     }
     if (duration <= 100ms && distance > 10px) {
          // Person flicked their finger
     }
     if (duration > 100ms && distance > 10px) {
          // Person dragged their finger
     }
}

Suponho que esse seja o cerne da questão: faz sentido tentar suportar os dois modelos com uma única base de código. Vou atualizar minha pergunta com mais alguns cenários.
DA.

1
"Geralmente você não deseja misturar o toque padrão e o não toque" depois de passar por isso, eu concordo. O problema é que eles continuam criando dispositivos de toque com teclados, o que é uma dor de cabeça para os desenvolvedores.
DA.

Concordo que seria muito mais fácil fazer no início o toque final da decisão js ou nenhum toque. Que mundo fácil seria! Mas o que é isso sobre esses malditos dispositivos, clique e toque sensível? Vale a pena ter todas essas dores de cabeça por causa delas? É este o futuro que eu me pergunto?
Garavani

Não gosto da ideia de ter várias definições de funções. Atualmente, minha empresa está trabalhando em um projeto em que um iPad não é tratado como dispositivo móvel e você ainda precisa do evento touchend. Mas o touchend não funciona nas versões normais da área de trabalho. Portanto, a solução do @mottie é ótima para esse cenário.
Pete


3

Bem ... Tudo isso é super complicado.

Se você tem modernizr, é um acéfalo.

ev = Modernizr.touch ? 'touchstart' : 'click';

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});

2
Você pode explicar o que isso faz? Acredito que ele verifique se o dispositivo suporta toque e, se não, usa clique. O problema é (ou pelo menos era , quando eu escrevi a pergunta) foi que alguns dispositivos suportam ambos. E nesses dispositivos, ainda preciso lidar com ambos os eventos, pois o gatilho pode vir do toque ou do clique.
DA.

2
Eu recomendo um pouco mais de explicação.
Aaron Hall

2

Outra implementação para melhor manutenção. No entanto, essa técnica também fará event.stopPropagation (). O clique não é capturado em nenhum outro elemento que clicou por 100 ms.

var clickObject = {
    flag: false,
    isAlreadyClicked: function () {
        var wasClicked = clickObject.flag;
        clickObject.flag = true;
        setTimeout(function () { clickObject.flag = false; }, 100);
        return wasClicked;
    }
};

$("#myButton").bind("click touchstart", function (event) {
   if (!clickObject.isAlreadyClicked()) {
      ...
   }
}

2

Apenas para fins de documentação, eis o que eu fiz para a solução de clique / resposta mais rápida / mais rápida na área de trabalho / toque no celular que eu conseguia pensar:

Substituí a onfunção do jQuery por uma modificada que, sempre que o navegador suporta eventos de toque, substitui todos os meus eventos de clique por touchstart.

$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
    on: (function(){
        var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
        return function( types, selector, data, fn, one ) {
            if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
            return this._on( types, selector, data, fn);
        };
    }()),
});

O uso seria exatamente o mesmo de antes, como:

$('#my-button').on('click', function(){ /* ... */ });

Mas usaria o touchstart quando disponível, clique quando não estiver. Não são necessários atrasos de qualquer tipo: D


1
O problema disso seriam os dispositivos que suportam AMBOS os teclados AND e onde você precisa garantir que esteja acomodando as duas interações.
DA.

Boa observação @DA. Adicionei uma verificação para substituir apenas o evento de clique se você não estiver usando nenhum evento de toque em combinação com ele. O Stil não será uma solução para todos os projetos, mas tenho certeza que seria um bom ajuste para muitos.
Gerson Goulart

2

Eu apenas tive a idéia de memorizar se ontouchstartalguma vez foi acionado. Nesse caso, estamos em um dispositivo que suporta e deseja ignorar o onclickevento. Desde ontouchstartsempre deve ser acionado antes onclick , eu estou usando isso:

<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>


1
Como os usuários podem usar o toque e o mouse durante a mesma visita à página, o problema com esse snippet é que ele registra o sinalizador touchAvailable uma vez em vez de por evento. O plug-in jQuery de stackoverflow.com/a/26145343/328272 faz o mesmo que o seu código, mas pode indicar um evento de mouse se foi acionado por toque ou mouse em uma base de evento. Não é só isso. ao clicar, mas também à saída do mouse, que pode ser acionada segundos (ou minutos) após o mouseenter (ideal para menus desdobráveis ​​que devem expandir-se na passagem do mouse com gestos do mouse ou clicar ao usar o toque).
Lmeurs

2

Você pode tentar assim:

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);

o que acontece com os dispositivos que o apoio toque e mouse
Walle Cyril

2

No meu caso, isso funcionou perfeitamente:

jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
    jQuery(this).off('mouseup');
}
});

O principal problema foi quando, em vez disso, tentei com o clique, nos dispositivos de toque, acionar o clique e tocar ao mesmo tempo; se eu usar o clique, algumas funcionalidades não funcionavam em dispositivos móveis. O problema com o clique é que é um evento global que dispara o restante do evento, incluindo touchend.


1

Isso funcionou para mim, o celular ouve os dois, por isso evite o que é o evento de toque. a área de trabalho ouve apenas o mouse.

 $btnUp.bind('touchstart mousedown',function(e){
     e.preventDefault();

     if (e.type === 'touchstart') {
         return;
     }

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });

1

Sendo para mim a melhor resposta dada por Mottie, estou apenas tentando tornar seu código mais reutilizável, então esta é a minha contribuição:

bindBtn ("#loginbutton",loginAction);

function bindBtn(element,action){

var flag = false;
$(element).bind('touchstart click', function(e) {
    e.preventDefault();
    if (!flag) {
        flag = true;
        setTimeout(function() {
            flag = false;
        }, 100);
        // do something
        action();
    }
    return false;
});

0

Também estou trabalhando em um aplicativo da web Android / iPad, e parece que apenas o uso de "touchmove" é suficiente para "mover componentes" (não é necessário iniciar o touch). Ao desativar o touchstart, você pode usar .click (); do jQuery. Na verdade, está funcionando porque não foi sobrecarregado pelo touchstart.

Por fim, você pode binb .live ("touchstart", function (e) {e.stopPropagation ();}); para solicitar que o evento touchstart pare de se propagar, clique na sala () para ser acionado.

Funcionou para mim.


Como você desabilitaria touchstart?
punkrockbuddyholly

Eu acredito que você "poderia" fazer algo como: jQuery ("# ​​my_element"). On ('touchstart', function (e) {e.preventDefault ()});
Nembleton 23/09

0

Há muitas coisas a considerar ao tentar resolver esse problema. A maioria das soluções interrompe a rolagem ou não trata os eventos de clique fantasma corretamente.

Para uma solução completa, consulte https://developers.google.com/mobile/articles/fast_buttons

NB: Você não pode manipular eventos de clique fantasma por elemento. Um clique atrasado é acionado pelo local da tela. Portanto, se seus eventos de toque modificarem a página de alguma forma, o evento de clique será enviado para a nova versão da página.


0

Pode ser eficaz atribuir aos eventos 'touchstart mousedown'ou 'touchend mouseup'evitar efeitos colaterais indesejados do uso click.


0

Aproveitando o fato de que um clique sempre segue um evento de toque, eis o que eu fiz para me livrar do "clique fantasma" sem precisar usar tempos limite ou sinalizadores globais.

$('#buttonId').on('touchstart click', function(event){
    if ($(this).data("already")) {
        $(this).data("already", false);
        return false;
    } else if (event.type == "touchstart") {
        $(this).data("already", true);
    }
    //your code here
});

Basicamente, sempre que um ontouchstartevento é acionado no elemento, um sinalizador é definido e, em seguida, removido (e ignorado) quando o clique chega.


0

Por que não usar a API de eventos jQuery?

http://learn.jquery.com/events/event-extensions/

Eu usei esse evento simples com sucesso. É limpo, com espaço para nome e flexível o suficiente para melhorar.

var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var eventType = isMobile ? "touchstart" : "click";

jQuery.event.special.touchclick = {
  bindType: eventType,
  delegateType: eventType
};

1
É recomendável usar a detecção de recurso em vez de cheirar o agente do usuário.
Jinglesthula

O problema são os dispositivos que suportam toque e clique.
DA.

0

Se você estiver usando jQuery, o seguinte funcionou muito bem para mim:

var callback; // Initialize this to the function which needs to be called

$(target).on("click touchstart", selector, (function (func){
    var timer = 0;
    return function(e){
        if ($.now() - timer < 500) return false;
        timer = $.now();
        func(e);
    }
})(callback));

Outras soluções também são boas, mas eu estava vinculando vários eventos em um loop e precisava da função de auto-chamada para criar um fechamento apropriado. Além disso, eu não queria desativar a ligação, pois queria que ela pudesse ser invocada no próximo clique / toque inicial.

Pode ajudar alguém em situação semelhante!


0

Para recursos simples, basta reconhecer o toque ou clique em Eu uso o seguinte código:

var element = $("#element");

element.click(function(e)
{
  if(e.target.ontouchstart !== undefined)
  {
    console.log( "touch" );
    return;
  }
  console.log( "no touch" );
});

Isso retornará "touch" se o evento touchstart estiver definido e "no touch", se não estiver. Como eu disse, essa é uma abordagem simples para eventos de clique / toque exatamente isso.


0

Estou tentando isso e até agora ele funciona (mas eu estou apenas no Android / Phonegap, então ressalte o emptor)

  function filterEvent( ob, ev ) {
      if (ev.type == "touchstart") {
          ob.off('click').on('click', function(e){ e.preventDefault(); });
      }
  }
  $('#keypad').on('touchstart click', '.number, .dot', function(event) {
      filterEvent( $('#keypad'), event );
      console.log( event.type );  // debugging only
           ... finish handling touch events...
  }

Não gosto do fato de estar vinculando manipuladores a cada toque, mas todas as coisas consideradas como toques não acontecem com muita frequência (no tempo do computador!)

Eu tenho um monte de manipuladores como o do '#keypad', por isso, ter uma função simples que me permite lidar com o problema sem muito código é o motivo pelo qual eu segui esse caminho.



0

EDIT: Minha resposta anterior (com base nas respostas deste tópico) não era o caminho a percorrer para mim. Eu queria um submenu para expandir a entrada do mouse ou tocar no clique e recolher na licença do mouse ou outro clique no toque. Como os eventos do mouse normalmente são acionados após os eventos de toque, era meio difícil escrever ouvintes de eventos que suportam a tela de toque e a entrada do mouse ao mesmo tempo.

plugin jQuery: toque ou mouse

Acabei escrevendo um plugin jQuery chamado " Touch Or Mouse " (897 bytes compactado) que pode detectar se um evento foi invocado por uma tela sensível ao toque ou mouse (sem testar o suporte ao toque!). Isso permite o suporte da tela sensível ao toque e do mouse ao mesmo tempo e separa completamente seus eventos.

Dessa forma, o OP pode usar touchstartou touchendresponder rapidamente a cliques de toque e clickcliques invocados apenas por um mouse.

Demonstração

Primeiro é preciso fazer ie. o elemento do corpo rastreia eventos de toque:

$(document.body).touchOrMouse('init');

Os eventos do mouse são vinculados aos elementos da maneira padrão e, ao ligar $body.touchOrMouse('get', e), podemos descobrir se o evento foi chamado por uma tela sensível ao toque ou mouse.

$('.link').click(function(e) {
  var touchOrMouse = $(document.body).touchOrMouse('get', e);

  if (touchOrMouse === 'touch') {
    // Handle touch click.
  }
  else if (touchOrMouse === 'mouse') {
    // Handle mouse click.
  }
}

Veja o plug-in no trabalho em http://jsfiddle.net/lmeurs/uo4069nh .

Explicação

  1. Este plugin precisa ser chamado, ie. o elemento body para rastrear touchstarte touchendeventos, dessa forma o touchendevento não precisa ser disparado no elemento trigger (ou seja, um link ou botão). Entre esses dois eventos de toque, este plug-in considera qualquer evento de mouse invocado pelo toque.
  2. Os eventos do mouse são disparados somente depois touchend, quando um evento do mouse é disparado dentro de ghostEventDelay(opção, 1000ms por padrão) depois touchend, este plug-in considera que o evento do mouse é chamado pelo toque.
  3. Ao clicar em um elemento usando uma tela sensível ao toque, o elemento ganha o :activeestado. O mouseleaveevento é acionado somente depois que o elemento perde esse estado por ie. clicando em outro elemento. Como isso pode levar alguns segundos (ou minutos!) Após o mouseenterevento ter sido disparado, esse plug-in acompanha o último mouseenterevento de um elemento : se o último mouseenterevento foi chamado pelo toque, o mouseleaveevento a seguir também é considerado chamado pelo toque.

0

Aqui está uma maneira simples de fazer isso:

// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
  if (!$(document).data('trigger')) $(document).data('trigger', e.type);
  if (e.type===$(document).data('trigger')) {
    // Do your stuff here
  }
});

Você basicamente salva o primeiro tipo de evento que é acionado na propriedade 'trigger' no objeto de dados do jQuery que está anexado ao documento raiz e só é executado quando o tipo de evento é igual ao valor em 'trigger'. Em dispositivos sensíveis ao toque, a cadeia de eventos provavelmente seria 'touchstart' seguida de 'clique'; no entanto, o manipulador 'click' não será executado porque "click" não corresponde ao tipo de evento inicial salvo em 'trigger' ("touchstart").

A suposição, e eu acredito que seja segura, é que o seu smartphone não mudará espontaneamente de um dispositivo de toque para um dispositivo de mouse ou a torneira nunca será registrada porque o tipo de evento 'trigger' é salvo apenas uma vez por o carregamento da página e o "clique" nunca corresponderiam a "toque inicial".

Aqui está um código de código com o qual você pode brincar (tente tocar no botão em um dispositivo de toque - não deve haver atraso no clique): http://codepen.io/thdoan/pen/xVVrOZ

Também implementei isso como um plugin jQuery simples que também suporta a filtragem de descendentes do jQuery passando uma string de seletor:

// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
  var selector, handler;
  switch (typeof arg1) {
    case 'function':
      selector = null;
      handler = arg1;
      break;
    case 'string':
      selector = arg1;
      if (typeof arg2==='function') handler = arg2;
      else return;
      break;
    default:
      return;
  }
  this.on('click touchstart', selector, function(e) {
    if (!$(document).data('trigger')) $(document).data('trigger', e.type);
    if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
  });
};

Codepen: http://codepen.io/thdoan/pen/GZrBdo/


Por que você usou uma propriedade de dados no documento em vez de apenas usar uma variável?
Raphael Schweikert

@RaphaelSchweikert Provavelmente apenas por hábito. Não existe um caminho certo ou errado, mas eu gosto de usar $.data()para armazenar dados que podem ser acessados ​​por várias funções e ainda não pertencem ao escopo global. Uma vantagem é que, como estou armazenando-o em um elemento, ele naturalmente me fornece mais contexto.
Thdan

0

O melhor método que encontrei é gravar o evento de toque e fazer com que o evento chame o evento de clique normal programaticamente. Dessa forma, você terá todos os seus eventos de clique normais e precisará adicionar apenas um manipulador de eventos para todos os eventos de toque. Para cada nó que você deseja tornar tocável, basta adicionar a classe "touchable" a ele para chamar o manipulador de toque. Com o Jquery, funciona dessa maneira com alguma lógica para garantir que seja um evento de toque real e não um falso positivo.

$("body").on("touchstart", ".touchable", function() { //make touchable  items fire like a click event
var d1 = new Date();
var n1 = d1.getTime();
setTimeout(function() {
    $(".touchable").on("touchend", function(event) {
        var d2 = new Date();
        var n2 = d2.getTime();
        if (n2 - n1 <= 300) {
            $(event.target).trigger("click"); //dont do the action here just call real click handler
        }
    });
}, 50)}).on("click", "#myelement", function() {
//all the behavior i originally wanted
});
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.