Detectando que o navegador não possui mouse e é apenas de toque


149

Estou desenvolvendo um aplicativo da Web (não um site com páginas de texto interessante) com uma interface muito diferente para toque (o dedo oculta a tela quando você clica) e o mouse (depende muito da visualização instantânea). Como posso detectar que meu usuário não tem mouse para apresentar a interface correta? Pretendo deixar uma opção para as pessoas com mouse e toque (como alguns notebooks).

O recurso de evento de toque no navegador não significa que o usuário esteja usando um dispositivo de toque (por exemplo, o Modernizr não o corta). O código que responde corretamente à pergunta deve retornar false se o dispositivo tiver um mouse, caso contrário, true. Para dispositivos com mouse e toque, ele deve retornar falso (não apenas tocar)

Como observação lateral, minha interface de toque também pode ser adequada para dispositivos apenas com teclado, por isso é mais a falta de mouse que estou procurando detectar.

Para tornar a necessidade mais clara, aqui está a API que estou procurando implementar:

// Level 1


// The current answers provide a way to do that.
hasTouch();

// Returns true if a mouse is expected.
// Note: as explained by the OP, this is not !hasTouch()
// I don't think we have this in the answers already, that why I offer a bounty
hasMouse();

// Level 2 (I don't think it's possible, but maybe I'm wrong, so why not asking)

// callback is called when the result of "hasTouch()" changes.
listenHasTouchChanges(callback);

// callback is called when the result of "hasMouse()" changes.
listenHasMouseChanges(callback);


Eu acho que você precisa repensar seu design se quiser que um aplicativo seja aplicável a computadores e dispositivos móveis / touch, mas tenha comportamentos diferentes para cada um. Eu não acho que o que você está procurando é realmente possível neste momento, pois uma pesquisa rápida no Google por "javascript detect mouse" mostra uma postagem moderadamente útil no quirksmode.org para detectar vários estados do mouse (cliques, posição, etc), mas ZERO resulta se o mouse realmente existe ou não.
davethegr8

24
Talvez seja porque o Google não ajudou que eu pedi aqui.
Nraynaud 15/02/12

2
Você já tentou o document mouseenter do jquery? $ (document) .mouseenter (function (e) {alert ("mouse");});
Parag Gajjar

4
Depois de considerar quase uma dúzia de avenidas promissoras para rejeitar cada uma delas em poucos minutos, essa pergunta está me deixando esplendidamente louca.
22613 Jordan Gray

Respostas:


65

O principal problema é que você possui as seguintes classes diferentes de dispositivos / casos de uso:

  1. Mouse e carregamento de teclas (área de trabalho)
  2. Apenas toque (telefone / tablet)
  3. Mouse, teclado e toque (touch laptops)
  4. Toque e teclado (teclado bluetooth no tablet)
  5. Somente mouse (preferência de usuário / navegação desabilitada)
  6. Apenas teclado (preferência de usuário / navegação desabilitada)
  7. Toque e mouse (ou seja, eventos de foco da caneta Galaxy Note 2)

O pior é que é possível fazer a transição de algumas dessas classes para outras (conecta-se a um mouse, conecta-se ao teclado) ou um usuário pode parecer estar em um laptop normal até chegar e tocar na tela.

Você está certo ao supor que a presença de construtores de eventos no navegador não é uma boa maneira de avançar (e é um tanto inconsistente). Além disso, a menos que você esteja acompanhando um evento muito específico ou apenas tentando excluir algumas classes acima, usar os próprios eventos não é uma prova completa.

Por exemplo, digamos que você tenha descoberto que um usuário emitiu uma remoção de rato real (não a falsificada de eventos de toque, consulte http://www.html5rocks.com/en/mobile/touchandmouse/ ).

Então o que?

Você habilita estilos suspensos? Você adiciona mais botões?

De qualquer maneira, você está aumentando o tempo para o vidro, porque precisa esperar que um evento seja acionado.

Mas o que acontece quando o seu usuário nobre decide desconectar o mouse e entrar em contato direto com o usuário .. você espera que ele toque sua interface agora cheia e depois a altera logo que ele faz um esforço para identificar sua interface agora lotada?

Em forma de marcador, citando o stucox em https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101

  • Queremos detectar a presença de um mouse
  • Ae provavelmente não pode detectar antes de um evento ser disparado
  • Como tal, o que estamos detectando é se um mouse foi usado nesta sessão - não será imediatamente após o carregamento da página
  • Provavelmente também não podemos detectar que não há um mouse - ele seria indefinido até ser verdadeiro (acho que isso faz mais sentido do que defini-lo como falso até prova)
  • E provavelmente não podemos detectar se um mouse está desconectado no meio da sessão - isso será indistinguível do usuário que desistiu do mouse

Um aspecto à parte: o navegador sabe quando um usuário se conecta a um mouse / se conecta a um teclado, mas não o expõe ao JavaScript.

Isso deve levar você ao seguinte:

O rastreamento dos recursos atuais de um determinado usuário é complexo, não confiável e tem mérito duvidoso

A idéia de aprimoramento progressivo se aplica muito bem aqui, no entanto. Crie uma experiência que funcione sem problemas, independentemente do contexto do usuário. Em seguida, faça suposições com base nos recursos do navegador / consultas de mídia para adicionar funcionalidades que serão relativas no contexto assumido. A presença de um mouse é apenas uma das inúmeras maneiras pelas quais diferentes usuários em diferentes dispositivos experimentam seu site. Crie algo com mérito no seu kernel e não se preocupe muito com a forma como as pessoas clicam nos botões.


3
Ótima resposta. Felizmente, o usuário sempre tem uma tela! Eu acho que faz sentido criar uma interface em que isso se ajuste ao modo de interação atual do usuário. Em um laptop touch, faz sentido ajustar o aplicativo (ou seja, os :hoverelementos e coisas assim) quando o usuário alterna do mouse para o touch. Parece improvável que o usuário está usando o mouse + toque no momento exato o mesmo tempo (I média comoonn é como ter 2 mouses conectados ao mesmo computador hahaha)
Sebastien Lorber

@SebastienLorber - odeio dizer isso a você, mas os usuários nem sempre têm uma tela. ( É possível usar JavaScript para detectar se um leitor de tela está sendo executado em uma máquina de usuários? )
ashleedawg

57

Que tal ouvir um evento de remoção de mouse no documento. Até ouvir esse evento, você assume que o dispositivo é apenas de toque ou teclado.

var mouseDetected = false;
function onMouseMove(e) {
  unlisten('mousemove', onMouseMove, false);
  mouseDetected = true;
  // initializeMouseBehavior();
}
listen('mousemove', onMouseMove, false);

(Onde listene unlistendelegar para addEventListenerou attachEventconforme apropriado.)

Espero que isso não leve a muito jank visual, seria péssimo se você precisar de novos layouts massivos com base no modo ...


2
É uma boa ideia, mas, infelizmente, o atraso na resposta o tornará inutilizável quando a interface do usuário do aplicativo depender da disponibilidade de um mouse. Isso é especialmente verdadeiro se o aplicativo tiver um iframed, portanto, os eventos do mouse só o atingirão se o mouse se move sobre o próprio iframe ..
Jon Gjengset

7
Isso poderia funcionar se o aplicativo iniciar com uma tela inicial e um botão "continuar". Se o mouse se mover antes do primeiro evento de redução do mouse, você terá um mouse. Só falharia se o botão carregado diretamente sob o mouse e o usuário tivesse uma mão muito firme (mesmo que um pixel em movimento deva ser captado).
SpliFF

49
boa ideia, mas parece não funcionar nos nossos testes . iPads acionam esse evento.
10138 Jeff Atwood

3
@JeffAtwood, o que você acabou fazendo no seu caso?
Michael Haren

2
Os iPads definitivamente acionam o evento mousemove, logo antes do evento mousedown. Descobri que a contagem de ratos> 0 e a contagem de ratos == contagem de mouse são uma boa maneira de detectar nenhum mouse. Não consigo duplicar isso com um mouse real.
Peter Wooster

36

A partir de 2018, existe uma maneira boa e confiável de detectar se um navegador possui um mouse (ou dispositivo de entrada semelhante): recursos de interação de mídia CSS4 que agora são suportados por quase qualquer navegador moderno (exceto IE 11 e navegadores móveis especiais).

W3C:

O recurso de mídia do ponteiro é usado para consultar a presença e a precisão de um dispositivo apontador, como um mouse.

Veja as seguintes opções:

    /* The primary input mechanism of the device includes a 
pointing device of limited accuracy. */
    @media (pointer: coarse) { ... }

    /* The primary input mechanism of the device 
includes an accurate pointing device. */
    @media (pointer: fine) { ... }

    /* The primary input mechanism of the 
device does not include a pointing device. */
    @media (pointer: none) { ... }

    /* Primary input mechanism system can 
       hover over elements with ease */
    @media (hover: hover) { ... }

    /* Primary input mechanism cannot hover 
       at all or cannot conveniently hover 
       (e.g., many mobile devices emulate hovering
       when the user performs an inconvenient long tap), 
       or there is no primary pointing input mechanism */
    @media (hover: none) { ... }

    /* One or more available input mechanism(s) 
       can hover over elements with ease */
    @media (any-hover: hover) { ... }


    /* One or more available input mechanism(s) cannot 
       hover (or there are no pointing input mechanisms) */
    @media (any-hover: none) { ... }

As consultas de mídia também podem ser usadas em JS:

if(window.matchMedia("(any-hover: none)").matches) {
    // do sth
}

Palavras-chave:

Documentação do W3: https://www.w3.org/TR/mediaqueries-4/#mf-interaction

Suporte para navegador: https://caniuse.com/#search=media%20features

Problema semelhante: Detecte se um dispositivo cliente suporta os estados: hover e: focus


Pessoalmente, gosto dessa resposta, mas, a partir de agora (19/10), as consultas CSS @ hover e ponteiro CSS estão disponíveis apenas em ~ 85% dos dispositivos em todo o mundo, de acordo com o caniuse.com. Certamente não é ruim, é preferível 95% ou mais. Espero que isso se torne padrão em dispositivos em breve.
MQuiggGeorgia 13/10/19

1
@MQuiggGeorgia Basicamente, eu concordo com suas críticas, basicamente, elas ainda não são suportadas em todos os lugares. Ainda caniuse.com para mim diz que é suportado 91,2% ( caniuse.com/#feat=css-media-interaction ). Observando mais de perto, ele é suportado em qualquer lugar, exceto no IE 11 e em navegadores achatados especiais no celular. Para ser justo, isso é verdade para qualquer recurso moderno, pois a Microsoft parou de implementar os recursos do IE há muito tempo. Para o IE 11, você pode usar um fallback das outras respostas aqui.
Blackbam

23

A resposta da @ Wyatt é ótima e nos dá muito o que pensar.

No meu caso, eu escolhi ouvir a primeira interação, só então definir um comportamento. Portanto, mesmo se o usuário tiver um mouse, tratarei como dispositivo de toque se a primeira interação foi um toque.

Considerando a ordem dada na qual os eventos são processados :

  1. touchstart
  2. touchmove
  3. toque
  4. passe o rato por cima
  5. ratoeira
  6. mousedown
  7. mouseup
  8. clique

Podemos supor que, se o evento do mouse for acionado antes do toque, é um evento real do mouse, não um emulado. Exemplo (usando jQuery):

$(document).ready(function() {
    var $body = $('body');
    var detectMouse = function(e){
        if (e.type === 'mousedown') {
            alert('Mouse interaction!');
        }
        else if (e.type === 'touchstart') {
            alert('Touch interaction!');
        }
        // remove event bindings, so it only runs once
        $body.off('mousedown touchstart', detectMouse);
    }
    // attach both events to body
    $body.on('mousedown touchstart', detectMouse);
});

Isso funcionou para mim


Não funciona para mim, Ipad Safari (IOS8.3) também detecta um mouse com esse trecho
netzaffin

3
@netzaffin. Obrigado pelo feedback, achei mais consistente usando o mouse em vez do mouse. Você daria uma olhada nesse violino do seu IOS e me informaria o resultado? Felicidades jsfiddle.net/bkwb0qen/15/embedded/result
Hugo Silva

1
Se você tiver uma tela sensível ao toque com um mouse, somente o método de entrada usado primeiro será detectado.
0xcaff

11

Só é possível detectar se um navegador é capaz de tocar . Não há como saber se ele realmente possui uma tela de toque ou um mouse conectado.

Pode-se priorizar o uso ouvindo evento de toque em vez de evento de mouse se a capacidade de toque for detectada.

Para detectar o recurso de toque em vários navegadores:

function hasTouch() {
    return (('ontouchstart' in window) ||       // html5 browsers
            (navigator.maxTouchPoints > 0) ||   // future IE
            (navigator.msMaxTouchPoints > 0));  // current IE10
}

Então pode-se usar isso para verificar:

if (!hasTouch()) alert('Sorry, need touch!);

ou para escolher qual evento escutar:

var eventName = hasTouch() ? 'touchend' : 'click';
someElement.addEventListener(eventName , handlerFunction, false);

ou use abordagens separadas para toque versus não toque:

if (hasTouch() === true) {
    someElement.addEventListener('touchend' , touchHandler, false);

} else {
    someElement.addEventListener('click' , mouseHandler, false);

}
function touchHandler(e) {
    /// stop event somehow
    e.stopPropagation();
    e.preventDefault();
    window.event.cancelBubble = true;
    // ...
    return false; // :-)
}
function mouseHandler(e) {
    // sorry, touch only - or - do something useful and non-restrictive for user
}

Para o mouse, é possível detectar apenas se o mouse está sendo usado, não se existe ou não. Pode-se configurar uma bandeira global para indicar que o mouse foi detectado pelo uso (semelhante a uma resposta existente, mas um pouco simplificada):

var hasMouse = false;

window.onmousemove = function() {
    hasMouse = true;
}

(não é possível incluir mouseupou mousedownporque esse evento também pode ser acionado pelo toque)

Navegadores restringe o acesso a APIs de sistema de baixo nível, necessárias para detectar recursos como recursos de hardware do sistema em que está sendo usado.

Existe a possibilidade de talvez escrever um plug-in / extensão para acessá-los, mas via JavaScript e DOM essa detecção é limitada para essa finalidade e seria necessário escrever um plug-in específico para as várias plataformas de SO.

Portanto, em conclusão: essa detecção só pode ser estimada por um "bom palpite".


8

Quando as consultas de mídia nível 4 estiver disponível nos navegadores, poderemos usar as consultas "ponteiro" e "pairar" para detectar dispositivos com um mouse.

Se realmente queremos comunicar essas informações para Javascript, poderíamos usar uma consulta CSS para definir estilos específicos de acordo com o tipo de dispositivo e, em seguida, usar getComputedStyle no Javascript para ler esse estilo e derivar dele o tipo de dispositivo original.

Mas um mouse pode ser conectado ou desconectado a qualquer momento, e o usuário pode querer alternar entre o toque e o mouse. Portanto, talvez seja necessário detectar essa alteração e oferecer a alteração da interface ou fazê-lo automaticamente.


1
Especificamente, any-pointereany-hover permitirá investigar todos os recursos aplicáveis ​​do dispositivo. É bom dar uma olhada em como podemos resolver esse problema no futuro! :)
Jordan Gray

2
window.matchMedia ("(qualquer ponteiro: grosso)"). corresponde === verdadeiro?
4sn0k 02/10/2015

7

Como você planeja oferecer uma maneira de alternar entre as interfaces de qualquer maneira, seria possível simplesmente pedir ao usuário que clique em um link ou botão para "inserir" a versão correta do aplicativo? Então você pode se lembrar da preferência deles por futuras visitas. Não é de alta tecnologia, mas é 100% confiável :-)


2
Na verdade, é uma sugestão muito boa, mas atrasa o tempo até o usuário acessar a interface real. Além disso, terei que fornecer uma maneira de alternar após a escolha inicial. Acaba sendo mais trabalho do que se poderia simplesmente ser detectado ..
Jon Gjengset

1
Pedir ao usuário é claramente a melhor maneira - se não sempre a prova de falhas - e oferece a você um local conveniente para colocar notificações de atualização e outras coisas. Acho que você está pensando sobre o "problema" ..
T4NK3R

4

@SamuelRossille Infelizmente, nenhum navegador que eu saiba expor a existência (ou a falta dele) de um mouse.

Então, com isso dito, nós apenas temos que tentar fazer o melhor possível com a nossa opção existente ... eventos. Eu sei que não é exatamente o que você está procurando ... concordou que atualmente está longe de ser o ideal.

Podemos fazer o possível para descobrir se um usuário está usando um mouse ou toque a qualquer momento. Aqui está um exemplo rápido e sujo usando jQuery & Knockout:

//namespace
window.ns = {};

// for starters, we'll briefly assume if touch exists, they are using it - default behavior
ns.usingTouch = ko.observable(Modernizr.touch); //using Modernizr here for brevity.  Substitute any touch detection method you desire

// now, let's sort out the mouse
ns.usingMouse = ko.computed(function () {
    //touch
    if (ns.usingTouch()) {
        //first, kill the base mousemove event
        //I really wish browsers would stop trying to handle this within touch events in the first place
        window.document.body.addEventListener('mousemove', function (e) {
            e.preventDefault();
            e.stopImmediatePropagation();
        }, true);

        //remove mouse class from body
        $('body').removeClass("mouse");

        //switch off touch listener
        $(document).off(".ns-touch");

        // switch on a mouse listener
        $(document).on('mousemove.ns-mouse', function (e) {
            if (Math.abs(window.lastX - e.clientX) > 0 || window.lastY !== e.clientY) {
                ns.usingTouch(false);  //this will trigger re-evaluation of ns.usingMouse() and result in ns.usingMouse() === true
            }
        });

        return false;
    }
    //mouse
    else {
        //add mouse class to body for styling
        $('body').addClass("mouse");

        //switch off mouse listener
        $(document).off(".ns-mouse");

        //switch on a touch listener
        $(document).on('touchstart.ns-touch', function () { ns.usingTouch(true) });

        return true;
    }
});

//tests:
//ns.usingMouse()
//$('body').hasClass('mouse');

Agora você pode vincular / assinar usingMouse () e usingTouch () e / ou estilizar sua interface com a classe body.mouse . A interface irá alternar assim que um cursor do mouse for detectado e no touchstart.

Esperamos ter em breve algumas opções melhores dos fornecedores de navegadores.


2

O Tera-WURFL pode informar os recursos do dispositivo que está visitando seu site , comparando a assinatura do navegador com o banco de dados. Dê uma olhada, é grátis!


1
Isso não funcionará para dispositivos que podem ou não ter telas sensíveis ao toque e mouse. Por exemplo, um computador com Windows área de trabalho pode ser conectado a uma tela sensível ao toque, mas geralmente também tem um mouse, enquanto um comprimido pode também estar executando o Windows, mas pode não ter um mouse conectado ..
Jon Gjengset

@ Jonhoo Suponha que os sistemas operacionais de desktop tenham um mouse conectado. Afinal, eles devem oferecer suporte a uma ampla gama de softwares que não foram desenvolvidos com a tela sensível ao toque em mente.
Gigi

1
E os tablets com Windows 8 simples? Ou Linux? Ou laptops com Android?
Jon Gjengset

2
@ Jonhoo Obviamente, essa abordagem não é a ideal, mas ainda não existe uma maneira portátil de saber isso. Se alguém estiver executando um laptop com Android, presuma que ele seja compatível com toque. Se alguém estiver executando um tablet Windows8, suponha que ele seja compatível com o mouse (o sistema operacional deve emular o mouse para programas não sensíveis ao toque).
Gigi

Isso agora está tão desatualizado que não é mais relevante.
Engenheiro de

2

Por que você simplesmente não detecta se ele tem a capacidade de detectar toques e / ou reagir aos movimentos do mouse?

// This will also return false on
// touch-enabled browsers like Chrome
function has_touch() {
  return !!('ontouchstart' in window);
}

function has_mouse() {
  return !!('onmousemove' in window);
}

4
Porque alguns navegadores (IE9, por exemplo) relatam que a função existe mesmo que nunca seja acionada. Eu acredito que este também é o comportamento "correto".
Jon Gjengset

por que você usaria uma função? apenas has_touch = 'ontouchstart' in windowserá suficiente, e assim por diante.
vsync

Bem, ele funciona no Chrome 47 para OS X, pelo menos. Relatando não iniciar o toque.
Phreahead #

2

Isso funcionou para mim em uma situação semelhante. Basicamente, suponha que o usuário não tenha um mouse até que você veja uma série curta de movimentos de rato consecutivos, sem interferir com mouse ou mouse. Não é muito elegante, mas funciona.

var mousedown = false;
var mousemovecount = 0;
function onMouseDown(e){
    mousemovecount = 0;
    mousedown = true;
}
function onMouseUp(e){
    mousedown = false;
    mousemovecount = 0;
}
function onMouseMove(e) {
    if(!mousedown) {
        mousemovecount++;
        if(mousemovecount > 5){
            window.removeEventListener('mousemove', onMouseMove, false);
            console.log("mouse moved");
            $('body').addClass('has-mouse');
        }
    } else {
        mousemovecount = 0;
    }
}
window.addEventListener('mousemove', onMouseMove, false);
window.addEventListener('mousedown', onMouseDown, false);
window.addEventListener('mouseup', onMouseUp, false);


0

Como outros já apontaram, detectar definitivamente se eles têm ou não um mouse não é confiável. Isso pode mudar facilmente, dependendo do dispositivo. Definitivamente, é algo que você não pode fazer de forma confiável com um valor booleano verdadeiro ou falso, pelo menos em uma escala de documento.

Os eventos de toque e mouse são exclusivos. Portanto, isso pode ajudar um pouco na tomada de ações diferentes. O problema é que os eventos de toque estão mais próximos dos eventos de cima / baixo / movimento do mouse e também acionam um evento de clique.

Na sua pergunta, você diz que deseja passar o mouse para visualizar. Além disso, não conheço outros detalhes sobre sua interface. Estou assumindo que, com a falta de um mouse, você deseja que um toque seja visualizado, enquanto um clique executa uma ação diferente por causa da visualização instantânea.

Se for esse o caso, você pode adotar uma abordagem preguiçosa para a detecção:

Um evento onclick sempre será precedido por um evento onmouseover com o mouse. Portanto, anote que o mouse está sobre o elemento que foi clicado.

Você pode fazer isso com um evento onmousemove em todo o documento. Você pode usarevent.target para registrar em qual elemento o mouse está localizado. Em seus eventos onclick, você pode verificar se o mouse está ou não sobre o elemento que está sendo clicado (ou um filho do elemento).

A partir daí, você pode optar por confiar no evento click para ambos e executar uma ação A ou B, dependendo do resultado. A ação B pode ser nada se alguns dispositivos de toque não emitem um evento de clique (em vez disso, você teria que confiar nos eventos ontouch *).


0

Não acho que seja possível identificar um dispositivo apenas com toque (que eu saiba, é claro). O principal problema é que todos os eventos de mouse e teclado também são disparados por dispositivos de toque. Veja o exemplo a seguir, os dois alertas retornam true para dispositivos touch.

function is_touch_present() {
  return ('ontouchstart' in window) || ('onmsgesturechange' in window);
}

function is_mouse_present() {
  return (('onmousedown' in window) && ('onmouseup' in window) && ('onmousemove' in window) && ('onclick' in window) && ('ondblclick' in window) && ('onmousemove' in window) && ('onmouseover' in window) && ('onmouseout' in window) && ('oncontextmenu' in window));
}

alert("Touch Present: " + is_touch_present());
alert("Mouse Present: " + is_mouse_present());

2
O ipad do Safari retorna truepara'onmousedown' in window
vsync

0

A melhor ideia, na minha opinião, é o mousemoveouvinte (atualmente a melhor resposta). Eu acredito que esse método precisa ser um pouco modificado. É verdade que os navegadores baseados em toque simulam até o evento mousemove, como você pode ver nesta discussão do iOS , por isso devemos ter um pouco de cuidado.

Faz sentido que os navegadores baseados em toque apenas emulem esse evento quando o usuário toca na tela (o dedo do usuário está pressionado). Isso significa que devemos adicionar um teste durante o manipulador do mouse para ver qual botão do mouse está pressionado (se houver) durante o evento. Se nenhum botão do mouse estiver pressionado, podemos assumir com segurança que um mouse real está presente. Se um botão do mouse estiver pressionado, o teste permanecerá inconclusivo.

Então, como isso seria implementado? Esta pergunta mostra que o método mais confiável para examinar qual botão do mouse está pressionado durante uma remoção de mouse é realmente escutar três eventos no nível do documento: remoção de mouse, remoção de mouse e mouse. A seta para cima e para baixo definirá apenas um sinalizador booleano global. A mudança fará o teste. Se você tiver um movimento e o booleano for falso, podemos assumir que um mouse está presente. Veja a pergunta para exemplos exatos de código.

Um comentário final. Esse teste não é ideal porque não pode ser executado em tempo de carregamento. Portanto, eu usaria um método de aprimoramento progressivo, conforme sugerido anteriormente. Por padrão, mostre uma versão que não suporta a interface de foco específico do mouse. Se um mouse for descoberto, ative esse modo em tempo de execução usando JS. Isso deve parecer o mais transparente possível para o usuário.

Para suportar alterações na configuração do usuário (ou seja, o mouse foi desconectado), você pode testar periodicamente. Embora eu acredite que seja melhor, neste caso, simplesmente notificar o usuário sobre os 2 modos e permitir que os usuários alternem manualmente entre eles (bem como a opção móvel / desktop, que sempre pode ser revertida).


Obrigado por lá boas sugestões de solução ... Eu acho que o principal problema não sendo sovled eu vou ter que recorrer a um destes
Samuel Rossille

Infelizmente, o mousemove é acionado ao clicar em um ipad. Testado apenas com simulador. Para hasMouse () eu estava usando if (! ('Ontouchstart' na janela)) return true; mas não funciona para laptops compatíveis com toque.
precisa saber é o seguinte

@Chris G - iPad inferno ... (batendo a cabeça contra a parede)
vsync

0

Executei alguns testes em vários PCs, Linux, iPhone, telefones e guias Android. Estranho que não exista uma solução fácil à prova de balas. O problema surge quando alguns que possuem Touch e nenhum mouse ainda apresentam os eventos Touch e Mouse ao aplicativo. Como deseja suportar instâncias apenas com mouse e apenas com toque, deseja processar ambas, mas isso causa ocorrências duplas de interações do usuário. Se o mouse pode saber que o dispositivo não está presente no dispositivo, ele pode ignorar os eventos de mouse falso / inserido. Tentei definir sinalizador se MouseMove for encontrado, mas alguns navegadores lançam MouseMove falso, bem como MouseUp e MouseDown. Tentei examinar os registros de data e hora, mas achei que isso era muito arriscado. Conclusão: eu encontrei os navegadores que criavam os eventos de mouse falso sempre inseridos um único MouseMove antes da MouseDown inserida. Em 99,99% dos meus casos, ao executar em um sistema que possui um mouse real, há vários eventos MouseMove consecutivos - pelo menos dois. Portanto, controle se o sistema encontra dois eventos MouseMove consecutivos e declare que não há mouse presente se essa condição nunca for atendida. Provavelmente isso é muito simples, mas está funcionando em todas as minhas configurações de teste. Acho que vou continuar até encontrar uma solução melhor. - Jim W


0

Uma solução simples no jQuery para detectar o uso do mouse, que resolve o problema em que os dispositivos móveis também acionam o evento 'mousemove'. Basta adicionar um ouvinte do touchstart para remover o ouvinte do mousemove, para que ele não seja acionado ao tocar.

$('body').one('touchstart.test', function(e) {
  // Remove the mousemove listener for touch devices
  $('body').off('mousemove.test');
}).one('mousemove.test', function(e) {
  // MOUSE!
});

Obviamente, o dispositivo ainda pode ser touch AND mouse, mas o acima garante que um mouse real foi usado.


0

Acabei de encontrar uma solução que eu acho bastante elegante.

// flag as mouse interaction by default
var isMouse = true;

// detect a touch event start and flag it
$(window).on('touchstart', function () {
  // if triggers touchstart, toggle flag
  // since touch events come before mouse event / click
  // callback of mouse event will get in callback
  // `isMouse === false`
  isMouse = false;
});

// now the code that you want to write
// should also work with `mouse*` events
$('a.someClass').on('click', function () {
  if (isMouse) {
    // interaction with mouse event
    // ...
  } else {
    // reset for the next event detection
    // this is crucial for devices that have both
    // touch screen and mouse
    isMouse = true;

    // interaction with touch event
    // ...
  }
});

0

Corri para o mesmo problema, onde um único toque também foi registrado como um clique. Depois de ler os comentários das principais respostas votadas, criei minha própria solução:

var body = document.getElementsByTagName('body')[0];
var mouseCount = 0;

// start in an undefined state 
// (i use this to blend in elements once we decide what input is used)
var interactionMode = 'undefined';


var registerMouse = function() {
  // count up mouseCount every time, the mousemove event is triggered
  mouseCount++;

  // but dont set it instantly. 
  // instead wait 20 miliseconds (seems to be a good value for multiple move actions), 
  // if another mousemove event accoures switch to mouse as interaction 
  setTimeout(function() {
    // a touch event triggers also exactly 1 mouse move event.
    // So only if mouseCount is higher than 1 we are really moving the cursor by mouse.
    if (mouseCount > 1) {
      body.removeEventListener('mousemove', registerMouse);
      body.removeEventListener('touchend', registerTouch);

      interactionMode = 'mouse';
      console.log('now mousing');
      listenTouch();
    }

    // set the counter to zero again
    mouseCount = 0;
  }, 20);
};

var registerTouch = function() {
  body.removeEventListener('mousemove', registerMouse);
  body.removeEventListener('touchend', registerTouch);

  interactionMode = 'touch';
  console.log('now touching');
  mouseCount = 0;

  listenMouse();
};

var listenMouse = function() {
  body.addEventListener("mousemove", registerMouse);
};
var listenTouch = function() {
  body.addEventListener("touchend", registerTouch);
};

listenMouse();
listenTouch();

// after one second without input, assume, that we are touching
// could be adjusted to several seconds or deleted
// without this, the interactionMode would stay 'undefined' until first mouse or touch event
setTimeout(function() {
  if (!body.classList.contains('mousing') || body.classList.contains('touching')) {
    registerTouch();
  }
}, 1000);
/* fix, so that scrolling is possible */

html,
body {
  height: 110%;
}
Mouse or touch me

O único problema que encontrei é que você precisa poder rolar para detectar corretamente um evento de toque. uma única guia (toque) pode causar problemas.


0

Passei horas descobrindo esse problema para o meu aplicativo Phonegap e criei esse hack. Ele gera um aviso do console se o evento acionado for um evento "passivo", o que significa que não aciona nenhuma alteração, mas funciona! Eu estaria interessado em alguma idéia para melhorar ou um método melhor. Mas isso efetivamente permite que eu use $ .touch () universalmente.

$(document).ready(function(){
  $("#aButton").touch(function(origElement, origEvent){
    console.log('Original target ID: ' + $(origEvent.target).attr('id'));
  });
});

$.fn.touch = function (callback) {
    var touch = false;
    $(this).on("click", function(e){
        if (!touch)
        {
            console.log("I click!");
            let callbackReal = callback.bind(this);
            callbackReal(this, e);
        }else{
            touch = true;
        }
        touch = false;
    });
    $(this).on("touchstart", function(e){
        if (typeof e.touches != typeof undefined)
        {
            e.preventDefault();
            touch = true;
            console.log("I touch!");
            let callbackReal = callback.bind(this);
            callbackReal(this, e);
        }
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<button id="aButton">A Button</button>


0

o principal problema que vejo aqui é que a maioria dos dispositivos de toque aciona um evento de mouse junto com o toque de resposta correspondente (touchstart -> mousedown, touchmove -> mousemove, etc.). Para apenas os teclados, finalmente os modernos, eles têm um navegador genérico, portanto você não consegue nem detectar a presença da classe MouseEvent.

A solução menos dolorosa aqui seria, na minha opinião, exibir um menu no lançamento (com gerenciamento 'alt' apenas para usuários do teclado) e talvez armazenar a opção com localStorage / cookies / serverside ou então manter a mesma escolha na próxima hora que o visitante chega.


-4

Eu recomendo fortemente contra essa abordagem. Considere dispositivos com tela de toque e do tamanho de desktops e você tem um conjunto diferente de problemas para resolver.

Torne seu aplicativo utilizável sem o mouse (ou seja, sem visualização instantânea).


1
É exatamente isso que estou tentando fazer. Estou tentando criar uma interface que funcione da melhor maneira possível nos dois tablets (sem mouse) e com um mouse, mas essas interfaces são necessariamente muito diferentes.
Jon Gjengset

Eu concordo com ampla. Você pode usar uma detecção de dispositivo (como DeviceAtlas) e selecionar a interface oferecida no momento do carregamento.
Teemu Ikonen

-4

Gostaria de recomendar um script que me ajudou:

Eu tinha lido e tentado tudo o que era sugerido, sem resultados suficientes.

Então eu investiguei um pouco mais e encontrei este código - device.js

Estou usando isso no site do meu cliente, para detectar a existência do mouse:
( <html>deve ter desktopclasse) e parece muito bom, e para touchsuporte, eu apenas faço a verificação regular 'ontouchend' in documente uso as informações de ambas as detecções para assumir uma coisa específica de que preciso.


Isso é equivalente ao sniffing de UA, levantado várias vezes neste tópico. Ele não resolve o caso de dispositivos com mouse e toque como o Windows 8, e definitivamente não é uma prova de futuro.
Hugo Silva

Eu o uso para resolver esse caso EXATO que você mencionou no aplicativo de um cliente e funciona bem. é uma prova do futuro, porque é mantido por seu desenvolvedor.
vsync 20/05

2
O caso mencionado (laptop habilitado para toque) seria identificado pelo seu script como "Área de trabalho", mesmo que eu não estivesse usando um mouse. "é prova de futuro, porque é mantido por seu desenvolvedor" - acho que você perdeu completamente o ponto de "prova de futuro" aqui. E se você tivesse lido e tentado tudo como afirmou, teria notado que a resposta de Gigi já sugere que a UA cheira.
Hugo Silva

-7

Geralmente, é uma idéia melhor detectar se a função de mouseover é suportada em vez de detectar o tipo de SO / navegador. Você pode fazer isso simplesmente da seguinte maneira:

if (element.mouseover) {
    //Supports the mouseover event
}

Certifique-se de não fazer o seguinte:

if (element.mouseover()) {
    // Supports the mouseover event
}

Este último chamaria o método, em vez de verificar sua existência.

Você pode ler mais aqui: http://www.quirksmode.org/js/support.html


2
Eu realmente não sei o que obter da sua postagem, mas if ('onmouseover' em $ ('body') [0]) alert ('onmouseover'); exibe uma mensagem no iPhone também
nraynaud 20/10

1
Isso verifica apenas se a função de mouseover está definida, o que seria em quase todos os navegadores. Ele não detecta se um mouse está realmente presente.
Jon Gjengset
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.