Como remover / ignorar: estilo hover css em dispositivos touch

141

Quero ignorar todas as :hoverdeclarações CSS se um usuário visitar nosso site por meio de um dispositivo touch. Como o :hoverCSS não faz sentido, e pode até ser perturbador se um tablet o acionar com um clique / toque, pois pode permanecer até o elemento perder o foco. Para ser sincero, não sei por que os dispositivos sensíveis ao toque sentem a necessidade de disparar :hoverem primeiro lugar - mas isso é realidade, então esse problema também é realidade.

a:hover {
   color:blue;
   border-color:green;
   // etc. > ignore all at once for touch devices
}

Então, (como) posso remover / ignorar todas as :hoverdeclarações CSS de uma só vez (sem precisar conhecer cada uma) para dispositivos touch depois de tê-las declaradas?

Simon Ferndriger
fonte
Isto é semelhante ao Como evitar efeitos de foco pegajosas para botões em dispositivos sensíveis ao toque - no entanto, mais geral
Simon Ferndriger
1
Eu contorno isso com uma solução php onde descubro se o dispositivo de visualização é móvel ou não e uso a folha de estilo correta com várias alterações com base nisso. Mas isso não responde à sua pergunta. Você pode usar as consultas do @media, mas isso não garante que os telefones e tablets tenham resoluções full hd.
TomFirth
1
Possível duplicado de efeitos de foco Desativar em navegadores móveis
rnevius

Respostas:

170

tl; dr use isso: https://jsfiddle.net/57tmy8j3/

Se você está interessado no porquê ou em que outras opções existem, continue lendo.

Quick'n'dirty - remove: pairar estilos usando JS

Você pode remover todas as regras CSS que contêm :hoverJavascript. Isso tem a vantagem de não precisar tocar em CSS e ser compatível mesmo com navegadores mais antigos.

function hasTouch() {
  return 'ontouchstart' in document.documentElement
         || navigator.maxTouchPoints > 0
         || navigator.msMaxTouchPoints > 0;
}

if (hasTouch()) { // remove all the :hover stylesheets
  try { // prevent exception on browsers not supporting DOM styleSheets properly
    for (var si in document.styleSheets) {
      var styleSheet = document.styleSheets[si];
      if (!styleSheet.rules) continue;

      for (var ri = styleSheet.rules.length - 1; ri >= 0; ri--) {
        if (!styleSheet.rules[ri].selectorText) continue;

        if (styleSheet.rules[ri].selectorText.match(':hover')) {
          styleSheet.deleteRule(ri);
        }
      }
    }
  } catch (ex) {}
}

Limitações: as folhas de estilo devem estar hospedadas no mesmo domínio (isso significa que não há CDNs). Desativa o foco em dispositivos mistos de mouse e toque como o Surface ou iPad Pro, o que prejudica o UX.

Somente CSS - use consultas de mídia

Coloque todas as suas regras: hover em um @mediabloco:

@media (hover: hover) {
  a:hover { color: blue; }
}

ou, como alternativa, substitua todas as suas regras de foco instantâneo (compatíveis com navegadores mais antigos):

a:hover { color: blue; }

@media (hover: none) {
  a:hover { color: inherit; }
}

Limitações: funciona apenas no iOS 9.0+, Chrome para Android ou Android 5.0+ ao usar o WebView. hover: hoverquebra os efeitos de pairar em navegadores antigos, hover: noneprecisa substituir todas as regras CSS definidas anteriormente. Ambos são incompatíveis com dispositivos mistos de mouse e toque .

O mais robusto - detectar toque via JS e preceder CSS: regras de foco

Este método precisa anexar todas as regras de foco com body.hasHover. (ou um nome de classe de sua escolha)

body.hasHover a:hover { color: blue; }

A hasHoverclasse pode ser adicionada usando hasTouch()o primeiro exemplo:

if (!hasTouch()) document.body.className += ' hasHover'

No entanto, isso deve ter as mesmas desvantagens dos dispositivos de toque misto dos exemplos anteriores, o que nos leva à solução definitiva. Ative efeitos de foco instantâneo sempre que um cursor do mouse for movido, desative efeitos de foco instantâneo sempre que um toque for detectado.

function watchForHover() {
  // lastTouchTime is used for ignoring emulated mousemove events
  let lastTouchTime = 0

  function enableHover() {
    if (new Date() - lastTouchTime < 500) return
    document.body.classList.add('hasHover')
  }

  function disableHover() {
    document.body.classList.remove('hasHover')
  }

  function updateLastTouchTime() {
    lastTouchTime = new Date()
  }

  document.addEventListener('touchstart', updateLastTouchTime, true)
  document.addEventListener('touchstart', disableHover, true)
  document.addEventListener('mousemove', enableHover, true)

  enableHover()
}

watchForHover()

Isso deve funcionar basicamente em qualquer navegador e habilita / desabilita os estilos de foco, conforme necessário.

Aqui está o exemplo completo - moderno: https://jsfiddle.net/57tmy8j3/
Legado (para uso com navegadores antigos): https://jsfiddle.net/dkz17jc5/19/

lâmina
fonte
Obrigado! Apenas um comentário: não seria melhor verificar primeiro se document.styleSheetsexiste, em vez de usar o try/catch?
Simon Ferndriger
1
Eu usei try/catchapenas no caso de algum erro estranho aparecer, que poderia travar o script, como deleteRule não funcionando por causa da política de mesma origem ou suporte inadequado no IE (editei o comentário para refletir isso). Mas sim, na maioria dos casos, apenas uma verificação simples deve ser suficiente.
blade
1
Vale ressaltar que os navegadores de desktop, incluindo o Firefox, não resultarão em 'notouch'. Eles relatam a compreensão dos eventos de toque.
Harry B
3
Esta solução não funciona para dispositivos mistos onde pairar deve trabalhar para o mouse, mas não para eventos de toque
NBAR
2
@Haneev, está correto na maioria dos casos, embora não no seu específico. Fazer sua regra funcionar adicionaria muita complexidade ao código, o que tornaria a resposta mais difícil de entender. Eu sugeriria dividir as regras ou usar a segunda solução, que é mais robusta de qualquer maneira. No entanto, você pode sugerir uma edição, se puder melhorar a primeira solução sem torná-la muito complicada.
lâmina
43

Adaptação do ponteiro para o resgate!

Como isso não foi tocado por um tempo, você pode usar:

a:link {
   color: red;
}

a:hover {
   color:blue;
}

@media (hover: none) {
   a:link {
      color: red;
   }
}

Veja esta demonstração no navegador da área de trabalho e no telefone . Suportado por modernos dispositivos de toque .

Nota : Lembre-se de que, como a entrada principal (capacidade) de um PC de superfície é um mouse, ele acabará sendo um link azul, mesmo que seja uma tela desanexada (tablet). Os navegadores (sempre) devem sempre padronizar a capacidade da entrada mais precisa.

Jason T Featheringham
fonte
Obrigado, isso seria conveniente! No entanto, eu tentei sair com o Chrome no modo móvel, mas não deu certo - quando "bateu", ele foi assim mesmo azul ...
Simon Ferndriger
1
@SimonFerndriger Isso é apenas uma limitação das ferramentas do Chrome. Tente abrir o link em um dispositivo apenas com toque real.
Jason T Featheringham
1
Funcionando perfeitamente no iPad Chrome / Safari vs. OS X Chrome / Safari. Obrigado! Finalmente, a solução elegante que você deseja para um problema visual tão pequeno.
Rakaloof
3
Eu apenas tentei isso no meu iPhone 6S real executando o iOS 11, e o link ficou azul.
Lukas Petr
1
Também tentei isso com o Nexus 5X executando o Android 8.1.0 e o Chrome 68.0.3440.91. O link ficou azul.
Trevin Avery 29/08
12

Eu encontrei o mesmo problema (no meu caso com navegadores móveis Samsung) e, portanto, me deparei com essa pergunta.

Graças à resposta de Calsal, encontrei algo que, na minha opinião, excluirá praticamente todos os navegadores de desktop porque parece ser reconhecido pelos navegadores móveis que experimentei (veja a captura de tela de uma tabela compilada: tabela de detecção de recurso de ponteiro CSS ).

Os documentos da Web MDN afirmam que

O recurso CSS @media do ponteiro pode ser usado para aplicar estilos com base em se o mecanismo de entrada principal do usuário é um dispositivo apontador e, em caso afirmativo, qual a precisão dele

.

O que descobri é o ponteiro: grosso é algo desconhecido para todos os navegadores de desktop na tabela anexa, mas conhecido por todos os navegadores móveis na mesma tabela. Essa parece ser a opção mais eficaz, porque todos os outros valores de palavras-chave de ponteiro fornecem resultados inconsistentes.

Portanto, você pode construir uma consulta de mídia como Calsal descrita, mas ligeiramente modificada. Ele usa uma lógica invertida para descartar todos os dispositivos de toque.

Sass mixin:

@mixin hover-supported {    
    /* 
     * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer 
     * coarse: The primary input mechanism includes a pointing device of limited accuracy.
     */
    @media not all and (pointer: coarse) {
        &:hover {
            @content;
        }
    }
}

a {
    color:green;
    border-color:blue;

    @include hover-supported() {
        color:blue;
        border-color:green;
    }
}

CSS compilado:

a {
  color: green;
  border-color: blue;
}
@media not all and (pointer: coarse) {
  a:hover {
    color: blue;
    border-color: green;
  }
}

Também é descrito nesta essência que criei após pesquisar o problema. Codepen para pesquisa empírica.

ATUALIZAÇÃO : Ao escrever esta atualização, 23/08/2018, e apontado por @DmitriPavlutin, essa técnica não parece mais funcionar com a área de trabalho do Firefox.

ProgrammerPer
fonte
Obrigado @DmitriPavlutin, de acordo com a tabela (quando foi compilada) deveria, mas eu poderia dar outra olhada.
ProgrammerPer
@DmitriPavlutin Você está realmente correto. Atualizo a resposta
ProgrammerPer
1
Você quer dizer que ele não funciona no Firefox Desktop se usado com um dispositivo de toque exclusivo? Funciona muito bem com tudo o que tentei.
FuzzeeLowgeek 19/04
@FuzzeeLowgeek Parece bom! A última vez que verifiquei foi em 23/08/2018, portanto, se você puder confirmar isso com certeza, talvez eu possa atualizar a resposta novamente?
ProgrammerPer
9

De acordo com a resposta de Jason , podemos endereçar apenas dispositivos que não suportam pairar com consultas de mídia css pura. Também podemos abordar apenas dispositivos que suportam pairar, como a resposta de moogal em uma pergunta semelhante, com @media not all and (hover: none). Parece estranho, mas funciona.

Eu fiz um mix Sass com isso para facilitar o uso:

@mixin hover-supported {
    @media not all and (hover: none) {
        &:hover {
            @content;
        }
    }
}

Atualização 2019-05-15 : recomendo este artigo do Medium que percorre todos os dispositivos diferentes que podemos segmentar com CSS. Basicamente, é uma mistura dessas regras de mídia, combine-as para destinos específicos:

@media (hover: hover) {
    /* Device that can hover (desktops) */
}
@media (hover: none) {
    /* Device that can not hover with ease */
}
@media (pointer: coarse) {
    /* Device with limited pointing accuracy (touch) */
}
@media (pointer: fine) {
    /* Device with accurate pointing (desktop, stylus-based) */
}
@media (pointer: none) {
    /* Device with no pointing */
}

Exemplo para destinos específicos:

@media (hover: none) and (pointer: coarse) {
    /* Smartphones and touchscreens */
}

@media (hover: hover) and (pointer: fine) {
    /* Desktops with mouse */
}

Adoro mixins, é assim que uso o hover mixin para segmentar apenas dispositivos compatíveis:

@mixin on-hover {
    @media (hover: hover) and (pointer: fine) {
        &:hover {
            @content;
        }
    }
}

button {
    @include on-hover {
        color: blue;
    }
}
Calsal
fonte
isso não funciona. você pairou: nenhum na consulta de mídia para um mixin suportado por pairar? você precisa de ponteiro: grosso conforme a outra resposta aqui.
Allan Nienhuis
Você já tentou ou simplesmente diz que não funciona porque parece estranho? Foi por isso que escrevi "Parece estranho" .. E funciona para mim sem apontador: grosso.
Calsal 02/09/19
Tive alguns resultados inconsistentes com a abordagem CSS no meu OnePlus 5T (no Chrome e no Firefix). Isso funcionou bem no iOS, mas não consegui fazê-lo funcionar no OnePlus. Eu testei completamente usando o any-pointer e any-hover
Zeth 01/07
6

Estou lidando com um problema semelhante atualmente.

Há duas opções principais que me ocorrem imediatamente: (1) verificação de cadeia de usuários ou (2) manutenção de páginas móveis separadas usando um URL diferente e que os usuários escolham o que é melhor para eles.

  1. Se você puder usar uma linguagem de fita adesiva da Internet, como PHP ou Ruby, poderá verificar a sequência de usuários do dispositivo que está solicitando uma página e simplesmente servir o mesmo conteúdo, mas com um estilo em <link rel="mobile.css" />vez do normal.

As cadeias de usuários têm informações de identificação sobre navegador, renderizador, sistema operacional etc. Depende de você decidir quais dispositivos são "sensíveis ao toque" versus não sensíveis ao toque. Você pode encontrar essas informações disponíveis em algum lugar e mapeá-las em seu sistema.

R. Se você tem permissão para ignorar navegadores antigos , basta adicionar uma única regra ao css normal, não móvel, a saber: EDIT : Erk. Depois de fazer algumas experiências, descobri que a regra abaixo também desabilita a capacidade de seguir links nos navegadores da webkit, além de apenas desativar os efeitos estéticos - consulte http://jsfiddle.net/3nkcdeao/.
Como tal, você terá para ser um pouco mais seletivo quanto à forma como você modifica as regras para o caso móvel do que eu mostro aqui, mas pode ser um ponto de partida útil:

* { 
    pointer-events: none !important; /* only use !important if you have to */
}

Como nota explicativa, desabilitar eventos de ponteiro em um pai e ativá-los explicitamente em um filho atualmente faz com que quaisquer efeitos de foco no pai se tornem ativos novamente se um elemento filho entrar :hover.
Vejo http://jsfiddle.net/38Lookhp/5/

B. Se você estiver dando suporte a renderizadores da Web herdados, terá que fazer um pouco mais de trabalho ao remover as regras que definem estilos especiais durante :hover. Para economizar tempo de todos, convém criar um comando de cópia + sediar automatizado que você executa nas folhas de estilo padrão para criar as versões móveis. Isso permitiria que você escrevesse / atualizasse o código padrão e eliminasse quaisquer regras de estilo usadas :hoverna versão móvel de suas páginas.

  1. (I) Como alternativa, basta informar seus usuários de que você possui um m.website.com para dispositivos móveis, além do website.com . Embora o subdomínio seja a maneira mais comum, você também pode ter alguma outra modificação previsível de um determinado URL para permitir que usuários móveis acessem as páginas modificadas. Nesse estágio, você deve ter certeza de que eles não precisam modificar o URL toda vez que navegam para outra parte do site.

Novamente aqui, você poderá adicionar uma regra extra ou duas às folhas de estilo ou ser forçado a fazer algo um pouco mais complicado usando o sed ou um utilitário semelhante. Provavelmente seria mais fácil de aplicar: não às regras de estilo, como as div:not(.disruptive):hover {...que você adicionaria class="disruptive"a elementos que fazem coisas irritantes para usuários móveis usando js ou a linguagem do servidor, em vez de alterar o CSS.

  1. (II) Na verdade, você pode combinar os dois primeiros e (se você suspeitar que um usuário passou para a versão errada de uma página), pode sugerir que eles entrem / saiam da exibição do tipo móvel ou simplesmente tenham um link em algum lugar permite aos usuários fracassar. Como já foi dito, as consultas @media também podem ser úteis para determinar o que está sendo usado para visitar.

  2. (III) Se você optar por uma solução jQuery depois de saber quais dispositivos são "touch" e quais não são, poderá achar que o hover CSS não é ignorado nos dispositivos touch-screen .

Raramente
fonte
pointer-events- isso é incrível! Era exatamente isso que eu procurava, muito obrigado!
Simon Ferndriger
1
@SimonFerndriger Bem, espero que seja útil daqui para frente. Lembre-se da ressalva de que, até o momento, os <a>links podem estar inacessíveis em pointer-events:none. Com alguma sorte, isso mudará mais tarde - ou o CSS 3.x ou 4.0+ terá pointer-events:navigation-onlyou similar.
SeldomNeedy
6

tente isto:

@media (hover:<s>on-demand</s>) {
    button:hover {
        background-color: #color-when-NOT-touch-device;
    }
}

ATUALIZAÇÃO: infelizmente o W3C removeu essa propriedade das especificações ( https://github.com/w3c/csswg-drafts/commit/2078b46218f7462735bb0b5107c9a3e84fb4c4b1 ).

Marouen Mhiri
fonte
Isso realmente resolveria o problema. Eu nunca ouvi falar disso antes. Em qual versão do navegador isso deve funcionar?
Simon Ferndriger
3
@SimonFerndriger caniuse.com/#feat=css-media-interaction e mais algumas informações: dev.opera.com/articles/media-features
Skeptic
Bom ... Não possui um amplo suporte ao navegador.
Giacomo Paita
Isso é legal! Obrigado por essa solução conveniente. Espero que seja adicionado aos padrões do W3C.
GProst
feliz que pude ajudar ^^
Marouen Mhiri
5

Você pode usar o Modernizr JS (consulte também esta resposta do StackOverflow ) ou criar uma função JS personalizada:

function is_touch_device() {
 return 'ontouchstart' in window        // works on most browsers 
  || navigator.maxTouchPoints;       // works on IE10/11 and Surface
};

if ( is_touch_device() ) {
  $('html').addClass('touch');
} else {
  $('html').addClass('no-touch');
} 

para detectar o suporte ao evento touch no navegador e, em seguida, atribuir uma CSSpropriedade regular , percorrendo o elemento com a html.no-touchclasse, assim:

html.touch a {
    width: 480px;
}

/* FOR THE DESKTOP, SET THE HOVER STATE */
html.no-touch a:hover {
   width: auto;
   color:blue;
   border-color:green;
}
Giacomo Paita
fonte
Isso também foi sugerido pelo @blade - no entanto, o uso da htmltag em vez da bodytag pode facilitar a leitura. Essa é realmente a solução que estou usando atualmente. Obrigado mesmo assim.
Simon Ferndriger
1

Foi útil para mim: link

function hoverTouchUnstick() {
    // Check if the device supports touch events
    if('ontouchstart' in document.documentElement) {
        // Loop through each stylesheet
        for(var sheetI = document.styleSheets.length - 1; sheetI >= 0; sheetI--) {
            var sheet = document.styleSheets[sheetI];
            // Verify if cssRules exists in sheet
            if(sheet.cssRules) {
                // Loop through each rule in sheet
                for(var ruleI = sheet.cssRules.length - 1; ruleI >= 0; ruleI--) {
                    var rule = sheet.cssRules[ruleI];
                    // Verify rule has selector text
                    if(rule.selectorText) {
                        // Replace hover psuedo-class with active psuedo-class
                        rule.selectorText = rule.selectorText.replace(":hover", ":active");
                    }
                }
            }
        }
    }
}
mineroot
fonte
1

Essa também é uma solução possível, mas você terá que passar pelo seu css e adicionar uma .no-touchclasse antes dos seus estilos de foco.

Javascript:

if (!("ontouchstart" in document.documentElement)) {
document.documentElement.className += " no-touch";
}

Exemplo de CSS:

<style>
p span {
    display: none;
}

.no-touch p:hover span {
    display: inline;
}
</style>
<p><a href="/">Tap me</a><span>You tapped!</span></p>

Fonte

Ps Mas devemos lembrar que há cada vez mais dispositivos de toque no mercado, que também suportam a entrada do mouse ao mesmo tempo.

mr.mii
fonte
0

Talvez ainda não seja uma solução perfeita (e é com o jQuery), mas talvez seja uma direção / conceito a ser trabalhado: que tal fazer o contrário? O que significa desativar os estados: hover css por padrão e ativá-los se um evento de remoção do mouse for detectado em qualquer lugar do documento. Claro que isso não funciona se alguém desativou js. O que mais poderia dizer contra fazê-lo desta maneira?

Talvez assim:

CSS:

/* will only work if html has class "mousedetected" */
html.mousedetected a:hover {
   color:blue;
   border-color:green;
}

jQuery:

/* adds "mousedetected" class to html element if mouse moves (which should never happen on touch-only devices shouldn’t it?) */
$("body").mousemove( function() {
    $("html").addClass("mousedetected");
});
lars na estrutura ascendente
fonte
1
Um problema é que também é .mousemove () ao clicar em algo em dispositivos touch.
Lars at upstruct
1
E você provavelmente disparará esse evento sem parar, afetando o desempenho. Se isso iria funcionar, do que deveria melhor aproveitamento$('body').one.('mousemove', ...)
Simon Ferndriger
0

Tente isso (eu uso o plano de fundo e a cor de plano de fundo neste exemplo):

var ClickEventType = ((document.ontouchstart !== null) ? 'click' : 'touchstart');

if (ClickEventType == 'touchstart') {
            $('a').each(function() { // save original..
                var back_color = $(this).css('background-color');
                var background = $(this).css('background');
                $(this).attr('data-back_color', back_color);
                $(this).attr('data-background', background);
            });

            $('a').on('touchend', function(e) { // overwrite with original style..
                var background = $(this).attr('data-background');
                var back_color = $(this).attr('data-back_color');
                if (back_color != undefined) {
                    $(this).css({'background-color': back_color});
                }
                if (background != undefined) {
                    $(this).css({'background': background});
                }
            }).on('touchstart', function(e) { // clear added stlye="" elements..
                $(this).css({'background': '', 'background-color': ''});
            });
}

css:

a {
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
HU TanKy
fonte
0

O caminho sujo ... Não é elegante, mas é o caminho mais fácil que pode salvar você.

Remova qualquer coisa que caracterize o foco.

.your-class:hover:before {
  color: blue;
  background: linear-gradient(to bottom, rgba(231,56,39,0) 0%, #aaaaaa 100%);
}

@media all and (min-width:320px) and (max-width: 960px) {
    .your-class:hover:before {
    color: black;
    background: transparent;
  }
}
Fellipe Sanches
fonte
0

Se o seu problema for quando você tocar / tocar no Android e em toda a div coberta pela cor transparente azul! Então você precisa apenas mudar o

CURSOR: PONTEIRO; para CURSOR: DEFAULT;

use mediaQuery para ocultar no celular / tablet.

Isso funciona para mim.

kunal Sharma
fonte
-1

Experimente esta solução fácil de jquery 2019, embora já exista algum tempo;

  1. adicione este plugin à cabeça:

    src = "https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"

  2. adicione isso ao js:

    $ ("*"). on ("touchend", função (e) {$ (this) .focus ();}); // se aplica a todos os elementos

  3. algumas variações sugeridas para isso são:

    $ (": input,: checkbox"). on ("touchend", função (e) {(this) .focus);}); // especificar elementos

    $ ("*"). on ("clique, toque", função (e) {$ (this) .focus ();}); // incluir evento de clique

    css: corpo {cursor: ponteiro; } // toque em qualquer lugar para finalizar o foco

Notas

  • coloque o plugin antes do bootstrap.js para evitar afetar as dicas de ferramentas
  • testado apenas no iphone XR ios 12.1.12 e ipad 3 ios 9.3.5, usando o Safari ou o Chrome.

Referências:

https://code.jquery.com/ui/

https://api.jquery.com/category/selectors/jquery-selector-extensions/

darren
fonte
2
Em 2019, você não precisará usar o jQuery - a WebAPI é bastante poderosa e suporta todos os principais recursos.
Acme #
Ya ... jQuery em 2019? Que tal um pouco de Flash? Hehehe.
remed.io