Como simular um pairar com um toque em navegadores ativados por toque?

120

Com algum HTML como este:

<p>Some Text</p>

Então, alguns CSS como este:

p {
  color:black;
}

p:hover {
  color:red;
}

Como posso permitir que um toque longo em um dispositivo habilitado para toque replique o foco? Posso alterar a marcação / usar JS etc, mas não consigo pensar em uma maneira fácil de fazer isso.

Rich Bradshaw
fonte
Estou pensando especificamente no iPhone OS - é uma demonstração de animação CSS na qual, para muitos, é fácil usar o cursor do mouse em vez de cliques para começar.
Rich Bradshaw

Respostas:

172

OK, eu resolvi isso! Envolve alterar um pouco o CSS e adicionar algumas JS.

Usando o jQuery para facilitar:

$(document).ready(function() {
    $('.hover').on('touchstart touchend', function(e) {
        e.preventDefault();
        $(this).toggleClass('hover_effect');
    });
});

Em inglês: quando você inicia ou termina um toque, ative hover_effectou desative a turma .

Em seguida, em seu HTML, adicione um foco de classe a qualquer coisa com a qual você queira que isso funcione. No seu CSS, substitua qualquer instância de:

element:hover {
    rule:properties;
}

com

element:hover, element.hover_effect {
    rule:properties;
}

E apenas para maior utilidade, adicione isso ao seu CSS também:

.hover {
-webkit-user-select: none;
-webkit-touch-callout: none;        
}

Para parar o navegador, solicitando que você copie / salve / selecione a imagem ou o que for.

Fácil!

Rich Bradshaw
fonte
Isso é ótimo. Eu dividir a função domready-se embora porque toggleas coisas vai atrapalhar se o usuário toca um elemento e arrasta para outro (por exemplo, se eles tocaram o item errado e estão tentando cancelar o toque)
Jacksonkr
Eu removi touchende preventDefault()no meu site e funciona bem, mas agora os menus não são fechados automaticamente ao tocar fora do alvo.
Даниил Пронин
Eu também gostaria de receber alguns conselhos sobre como fechar esse menu quando o usuário tocar em outro lugar ou começar a rolar. Acho que poderia haver outro touchstartouvinte no corpo (ou similar), mas me perguntei se havia uma maneira melhor. Obrigado!
Darryl jovem
2
Existe alguma maneira de ignorar um gesto de rolagem? Isso faz exatamente o que eu preciso, mas agora não consigo rolar para cima e para baixo o conteúdo que tem esse efeito.
alsobubbly
1
Removai o preventDefault, pois desejo que a rolagem esteja disponível, mas obrigado pelas dicas sobre touchstart e touchend! Deus envie esta é a única resposta que funciona de maneira confiável em todos os navegadores.
Petraeus
54

Tudo o que você precisa fazer é vincular o touchstart a um pai. Algo assim vai funcionar:

$('body').on('touchstart', function() {});

Você não precisa fazer nada na função, deixe em branco. Isso será suficiente para colocar o mouse em contato, então um toque se comporta mais como: hover e menos como: active. Magia para iOS.

Razvan Cercelaru
fonte
2
Esta solução permite que o link seja executado no segundo toque?
samuelkobe
1
Não, ele aciona o clique no primeiro toque ainda. Eu apenas testei.
Jack
Solução incrível! Rápido e fácil.
Hunter Turner
41

Tente o seguinte:

<script>
document.addEventListener("touchstart", function(){}, true);
</script>

E no seu CSS:

element:hover, element:active {
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none;
-webkit-touch-callout: none /*only to disable context menu on long press*/
}

Com esse código, você não precisa de uma classe .hover extra!

Anselmo
fonte
1
isso funciona para mim .. algumas outras coisas que podem ajudar ainda mais isso são, em vez de adicionar javascript, faça o seguinte: <body ontouchstart = ""> adicione um temporizador pequeno no evento css: active: tr: active {background-color : # 118aab; transição: todos os .2s lineares; -webkit-tap-realce-cor: rgba (0,0,0,0); -webkit-user-select: nenhum; -webkit-touch-callout: none}
Kozy
Trabalhou para mim. Obrigado ... A única coisa - fica um pouco ... quero dizer - depois que você pressiona, ele não carrega instantaneamente.
Roman Losev
Use fastclick.js para remover o lag
Anselm
25

Para responder à sua pergunta principal: "Como simular um foco instantâneo com navegadores ativados por toque no toque?"

Basta permitir 'clicar' no elemento (tocando na tela) e acionar o hoverevento usando JavaScript.

var p = document.getElementsByTagName('p')[0];
p.onclick = function() {
 // Trigger the `hover` event on the paragraph
 p.onhover.call(p);
};

Isso deve funcionar, desde que haja um hoverevento no seu dispositivo (mesmo que normalmente não seja usado).

Atualização: Acabei de testar essa técnica no meu iPhone e ela parece funcionar bem. Experimente aqui: http://jsfiddle.net/mathias/YS7ft/show/light/

Se você deseja usar um 'toque longo' para acionar o foco, pode usar o snippet de código acima como ponto de partida e se divertir com temporizadores e outras coisas;)

Mathias Bynens
fonte
Eu não sabia sobre esse bit onhover.call (p), isso é muito legal. Embora não haja pairar fora ...
Rich Bradshaw
Você já testou isso com vários elementos e toque longo? Significa que o usuário move o dedo pela tela e mostra um estilo de foco para cada elemento?
Marcus
Parece que se você já estiver usando o jQuery, poderá chamar o evento hover com o próprio jQuery, por exemplo: jQuery.hover (); Não tenho certeza se a API suporta isso.
Kzqai #
Opa, isso foi um comentário mais aplicável à postagem acima que realmente usa jQuery, desculpe.
Kzqai #
Você precisa ter algum script especial ou chamar a função? Funciona para mim, mas apenas em uma página e não sei qual é a diferença.
Richard Young
13

Solução melhorada

Primeiro, fui com a abordagem de Rich Bradshaw , mas depois começaram a aparecer problemas. Ao fazer o e.preventDefault () no evento 'touchstart' , a página não rola mais e, nem a pressão prolongada é capaz de disparar o menu de opções, nem o zoom duplo clique é capaz de concluir a execução.

Uma solução poderia ser descobrir qual evento está sendo chamado e apenas e.preventDefault () no último, 'touchend' . Como o 'touchmove' do scroll vem antes do 'touchend', ele permanece como padrão e o 'click' também é impedido, pois vem com as senhas na cadeia de eventos aplicadas ao dispositivo móvel, da seguinte forma:

// Binding to the '.static_parent' ensuring dynamic ajaxified content
$('.static_parent').on('touchstart touchend', '.link', function (e) {

    // If event is 'touchend' then...
    if (e.type == 'touchend') {
        // Ensuring we event prevent default in all major browsers
        e.preventDefault ? e.preventDefault() : e.returnValue = false;
    }

    // Add class responsible for :hover effect
    $(this).toggleClass('hover_effect');
});

Mas então, quando o menu de opções aparece, ele não aciona mais o 'toque' responsável por alternar entre a classe e, da próxima vez, o comportamento do foco será o contrário, totalmente confuso.

Uma solução seria, novamente, descobrir condicionalmente em qual evento estamos, ou apenas realizar eventos separados, e usar addClass () e removeClass () respectivamente em 'touchstart' e 'touchend' , garantindo que ele sempre comece e termine por respectivamente adicionando e removendo em vez de decidir condicionalmente. Para finalizar, também vincularemos o retorno de chamada de remoção ao tipo de evento 'focusout' , permanecendo responsável por limpar a classe de foco de qualquer link que possa permanecer ativada e nunca revisitada novamente, da seguinte forma:

$('.static_parent').on('touchstart', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('touchend focusout', '.link', function (e) {
    // Think double click zoom still fails here
    e.preventDefault ? e.preventDefault() : e.returnValue = false;
    $(this).removeClass('hover_effect');
});

Atenção: Alguns erros ainda ocorrem nas duas soluções anteriores e, também pense (não testado), o zoom duplo clique também falha.

Arrumado e esperançosamente livre de erros (não :)) Solução Javascript

Agora , por um segundo, mais limpo, organizado e responsivo, use apenas javascript (sem combinação entre a classe .hover e pseudo: hover) e de onde você pode chamar diretamente seu comportamento do ajax no evento universal (móvel e desktop) 'click' , Encontrei uma pergunta muito bem respondida, da qual finalmente entendi como misturar eventos de toque e mouse sem vários retornos de chamada de eventos, inevitavelmente alterando os eventos uns dos outros na cadeia de eventos. Aqui está como:

$('.static_parent').on('touchstart mouseenter', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('mouseleave touchmove click', '.link', function (e) {
    $(this).removeClass('hover_effect');

    // As it's the chain's last event we only prevent it from making HTTP request
    if (e.type == 'click') {
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        // Ajax behavior here!
    }
});
Alexandre Rosário
fonte
3

O hoverefeito do mouse não pode ser implementado no dispositivo de toque. Quando eu apareci com a mesma situação safari ios, usei :activeno css para fazer efeito.

ie

p:active {
  color:red;
}

No meu caso, está funcionando. Pode ser que também seja o caso que pode ser usado sem o uso de javascript. Apenas tente.

sijo vijayan
fonte
2

Adicione esse código e defina a classe "tapHover" para os elementos que você gostaria de trabalhar dessa maneira. Na primeira vez que você toca em um elemento, ele obtém o pseudoclasse ": pairar" e a classe "tapped". O evento Click será impedido. Segunda vez que você tocar no mesmo elemento, clique em evento será acionado.

// Activate only in devices with touch screen
if('ontouchstart' in window)
{
    // this will make touch event add hover pseudoclass
    document.addEventListener('touchstart', function(e) {}, true);

    // modify click event
    document.addEventListener('click', function(e) {
        // get .tapHover element under cursor
        var el = jQuery(e.target).hasClass('tapHover')
            ? jQuery(e.target)
            : jQuery(e.target).closest('.tapHover');

        if(!el.length)
            return;

        // remove tapped class from old ones
        jQuery('.tapHover.tapped').each(function() {
            if(this != el.get(0))
                jQuery(this).removeClass('tapped');
        });

        if(!el.hasClass('tapped'))
        {
            // this is the first tap
            el.addClass('tapped');
            e.preventDefault();
            return false;
        }
        else
        {
            // second tap
            return true;
        }
    }, true);
}
.box {
	float:		left;
	
	display:	inline-block;
	margin:		50px 0 0 50px;
	width:		100px;
	height:		100px;
	overflow:	hidden;
	
	font-size:	20px;
	
	border:		solid 1px black;
}
.box.tapHover {
	background:	yellow;
}
.box.tapped {
	border:		solid 3px red;
}
.box:hover {
	background:	red;
}
<div class="box" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>

l00k
fonte
1

Sem o JS específico do dispositivo (ou melhor, do navegador), tenho certeza de que você está sem sorte.

Edit: pensei que você queria evitar isso até que eu reler sua pergunta. No caso do Mobile Safari, você pode se registrar para obter todos os eventos de toque semelhantes ao que você pode fazer com os UIView-s nativos. Não consegue encontrar a documentação no momento, mas tentará.

Joonas Trussmann
fonte
1

Uma maneira de fazer isso seria fazer o efeito de foco quando o toque iniciar e remover o efeito de foco quando o toque se mover ou terminar.

É o que a Apple tem a dizer sobre o manuseio do toque em geral, já que você menciona o iPhone.

desenhado
fonte
O conteúdo do link ainda é relevante agora?
Flavien Volken
1

Meu gosto pessoal é atribuir os :hoverestilos ao :focusestado também, como:

p {
    color: red;
}

p:hover, p:focus {
    color: blue;
}

Depois, com o seguinte HTML:

<p id="some-p-tag" tabindex="-1">WOOOO</p>

E o seguinte JavaScript:

$("#some-p-tag").on("touchstart", function(e){
    e.preventDefault();
    var $elem = $(this);

    if($elem.is(":focus")) {
        //this can be registered as a "click" on a mobile device, as it's a double tap
        $elem.blur()
    }
    else {
        $elem.focus();
    }
});
pimbrouwers
fonte
1

Resolvido 2019 - Passe o mouse sobre o Touch

Agora parece melhor evitar usar o hover completamente com o ios ou o touch em geral. O código abaixo aplica seu css desde que o toque seja mantido e sem outros menus desdobráveis ​​do ios. Faça isso;

  1. Jquery add: $ ("p"). On ("touchstart", função (e) {$ (this) .focus (); e.preventDefault ();});

  2. CSS: substitua p: hover por p: focus e adicione p: active

Opções;

  • substitua o jquery p selector por qualquer classe etc

  • para que o efeito permaneça, mantenha p: pairar também e adicione body {cursor: ponter;} para que um toque em qualquer lugar o termine

  • tente eventos de clique e mouseover, bem como toque inicial no mesmo código (mas não testado)

  • remover e.preventDefault (); para permitir que os usuários utilizem submenus ios, por exemplo, copiar

Notas

  • testado apenas para elementos de texto, o ios pode tratar entradas etc. de maneira diferente

  • testado apenas no iphone XR ios 12.1.12 e ipad 3 ios 9.3.5, usando o Safari ou o Chrome.

darren
fonte
0

Uma mistura de Javascript nativo e jQuery:

var gFireEvent = function (oElem,sEvent) 
{
 try {
 if( typeof sEvent == 'string' && o.isDOM( oElem ))
 {
  var b = !!(document.createEvent),
     evt = b?document.createEvent("HTMLEvents"):document.createEventObject();
  if( b )    
  {  evt.initEvent(sEvent, true, true ); 
    return !oElem.dispatchEvent(evt);
  }
  return oElem.fireEvent('on'+sEvent,evt);
 }
 } catch(e) {}
 return false;
};


// Next you can do is (bIsMob etc you have to determine yourself):

   if( <<< bIsMob || bIsTab || bisTouch >>> )
   {
     $(document).on('mousedown', function(e)
     {
       gFireEvent(e.target,'mouseover' );
     }).on('mouseup', function(e)
     {
       gFireEvent(e.target,'mouseout' );
     });
   }
Codebeat
fonte
1
desculpe a ser pedante, mas jQuery é Javacript
matpol
2
@matpol: jQuery é javascript, mas javascript não é jQuery. Especial para você, eu adiciono a palavra 'puro', javascript puro. Satisfeito, senhor?
Codebeat
você não pode usar o jquery sem usar o javascript 'puro'. Eu apenas afirmo porque algumas pessoas que freqüentam SO parecem não saber disso.
181313 matpol
5
Uma mistura de Javascript nativo e jQuery, satisfeito?
Codebeat
0

Solução mais fácil que encontrei: eu tinha algumas tags <span> com as regras: hover css. Eu mudei para <a href = "javascript: void (0)"> e voilà. Os estilos de foco no iOS começaram a funcionar.

Becario Senior
fonte
0

Use também pode usar CSS, adicione foco e ativo (para IE7 e inferior) ao link oculto. Exemplo de um menu ul dentro de uma div com menu de classe:

.menu ul ul {display:none; position:absolute; left:100%; top:0; padding:0;}
.menu ul ul ul {display:none; position:absolute; top:0; left:100%;}
.menu ul ul a, .menu ul ul a:focus, .menu ul ul a:active { width:170px; padding:4px 4%; background:#e77776; color:#fff; margin-left:-15px; }
.menu ul li:hover > ul { display:block; }
.menu ul li ul li {margin:0;}

É tarde e não testado, deve funcionar ;-)

Mark Groen
fonte