È possibile forzare l'ignoranza della pseudoclasse: hover per gli utenti di iPhone / iPad?


94

Ho alcuni menu CSS sul mio sito che si espandono con :hover(senza js)

Funziona in modo semi-rotto su iDevices, ad esempio un tocco attiverà la :hoverregola ed espanderà il menu, ma poi toccando altrove non rimuove il file :hover. Inoltre, se è presente un collegamento all'interno dell'elemento che è stato modificato :hover, è necessario toccare due volte per attivare il collegamento (il primo tocco attiva :hover, il secondo tocco attiva il collegamento).

Sono riuscito a far funzionare bene le cose su iPhone vincolando l' touchstartevento.

Il problema è che a volte mobile safari sceglie ancora di attivare la :hoverregola dal css invece che dai miei touchstarteventi!

So che questo è il problema perché quando disabilito :hovermanualmente tutte le regole nel css, il safari mobile funziona alla grande (ma i browser normali ovviamente non lo fanno più).

Esiste un modo per "annullare" dinamicamente le :hoverregole per determinati elementi quando l'utente è su mobile safari?

Vedere e confrontare il comportamento di iOS qui: http://jsfiddle.net/74s35/3/ Nota: solo alcune proprietà CSS attivano il comportamento a due clic, ad esempio display: none; ma non sfondo: rosso; o decorazione del testo: sottolineato;


7
PER FAVORE NON disabilitare il passaggio del mouse solo per la presenza di eventi "touch". Ciò influirà negativamente sugli utenti di laptop dotati di dispositivi di input touch e mouse. Vedi la mia risposta per maggiori dettagli
Simon_Weaver

Risposte:


77

Ho scoperto che ": hover" è imprevedibile in iPhone / iPad Safari. A volte il tocco su un elemento rende quell'elemento ": hover", mentre a volte si sposta su altri elementi.

Per il momento, ho solo un corso "no-touch" al corpo.

<body class="yui3-skin-sam no-touch">
   ...
</body>

E hai tutte le regole CSS con ": hover" sotto ".no-touch":

.no-touch my:hover{
   color: red;
}

Da qualche parte nella pagina, ho javascript per rimuovere la classe no-touch dal corpo.

if ('ontouchstart' in document) {
    Y.one('body').removeClass('no-touch');
}

Non sembra perfetto, ma funziona comunque.


5
Questo è l'unico modo per farlo. Quello che mi preoccupa è che inquina il sito "normale" con cose che non ci appartengono ed è irrilevante. Oh bene. Grazie.
Christopher Camps

Puoi anche utilizzare le media query con un fallback per IE
Lime

2
@ Shackrock: non credo che l'approccio "un tocco per passare il mouse e due per fare clic" sia una buona soluzione. Poiché in realtà non esiste alcun passaggio del mouse su un dispositivo touch (finché non vengono forniti con schermi che rilevano il passaggio del dito su di esso), penso che sia meglio non simulare affatto il passaggio del mouse. Per gli effetti, come quelli comuni su pulsanti e link, salta semplicemente l'effetto. Per i menu che si espandono al passaggio del mouse, fallo espandere al tocco / clic. Il mio 2c.
Adrian Schmidt

18
Recentemente ho riscontrato problemi in questo tipo di approccio a causa dei nuovi sistemi Windows 8 con input touch e mouse
Tom

4
+1 al commento di Tom: il controllo di ontouchstart restituirà true sui laptop con funzionalità touch, anche se collegati a un monitor esterno (dove un sito compatibile con il mouse con stati di hover è più desiderabile).
gregdev

45

:hovernon è il problema qui. Safari per iOS segue una regola molto strana. Spara mouseovere mousemoveprima; se qualcosa viene modificato durante questi eventi, il "clic" e gli eventi correlati non vengono attivati:

Diagramma dell'evento tocco in iOS

mouseentere mouseleavesembrano essere inclusi, sebbene non siano specificati nel grafico.

Se modifichi qualcosa a seguito di questi eventi, gli eventi clic non verranno attivati. Ciò include qualcosa di più in alto nell'albero DOM. Ad esempio, questo impedirà ai singoli clic di funzionare sul tuo sito web con jQuery:

$(window).on('mousemove', function() {
    $('body').attr('rel', Math.random());
});

Modifica: per chiarimenti, l' hoverevento di jQuery include mouseentere mouseleave. Entrambi impediranno clickse il contenuto viene modificato.


5
ovviamente: hover è il problema :-) o meglio l'implementazione buggata di Safari di esso. questo è un background interessante sulla questione, ma Apple è l'unica parte in grado di risolvere questo terribile comportamento. o deve "annullare il passaggio del mouse" quando si fa clic su qualcosa al di fuori degli elementi, oppure non "passare mai il mouse" in primo luogo. il comportamento corrente fondamentalmente rompe qualsiasi sito Web che utilizza: hover for a mouse
Simon_Weaver

questo mi ha aiutato a rendermi conto che il hovergestore jquery stava impedendo che un clickgestore separato venisse colpito - che scherzo!
Simon_Weaver

1
Risposta brillante e migliore spiegazione del problema che ho incontrato. Non è solo il passaggio del CSS che causa il problema del "doppio clic", ma letteralmente qualsiasi modifica DOM dopo il passaggio del mouse / mouseenter e altri eventi.
Oliver Joseph Ash

Ecco un esempio che mostra l'evento clic che non si mouseenter attiva
Oliver Joseph Ash

@Oliver Joseph Ash Right, da cui l'esempio JS nella risposta. ;)
Zenexer

18

La libreria di rilevamento delle funzionalità del browser Modernizer include un controllo per gli eventi di tocco.

Il comportamento predefinito è applicare classi al tuo elemento html per ogni funzionalità rilevata. È quindi possibile utilizzare queste classi per definire lo stile del documento.

Se gli eventi tocco non sono abilitati Modernizr può aggiungere una classe di no-touch:

<html class="no-touch">

E poi ambisci i tuoi stili di hover con questa classe:

.no-touch a:hover { /* hover styles here */ }

Puoi scaricare una build Modernizr personalizzata per includere il numero di rilevamenti di funzionalità di cui hai bisogno.

Ecco un esempio di alcune classi che possono essere applicate:

<html class="js no-touch postmessage history multiplebgs
             boxshadow opacity cssanimations csscolumns cssgradients
             csstransforms csstransitions fontface localstorage sessionstorage
             svg inlinesvg no-blobbuilder blob bloburls download formdata">

Tieni presente che nelle versioni recenti di Chrome su desktop Modernizr segnala "tocco". Questo è apparentemente corretto nella versione 3 con touchevents.
Craig Jacobs

18

Alcuni dispositivi (come altri hanno già detto) hanno eventi di tocco e mouse. Il Microsoft Surface, ad esempio, ha un touch screen, un trackpad E uno stilo che in realtà genera eventi di hover quando si passa sopra lo schermo.

Qualsiasi soluzione che disabiliti :hover base alla presenza di eventi "touch" interesserà anche gli utenti di Surface (e molti altri dispositivi simili). Molti nuovi laptop sono touch e rispondono agli eventi di tocco, quindi disabilitare il passaggio del mouse è davvero una cattiva pratica.

Questo è un bug in Safari, non c'è assolutamente alcuna giustificazione per questo terribile comportamento. Mi rifiuto di sabotare i browser non iOS a causa di un bug in iOS Safari che apparentemente esiste da anni. Spero davvero che risolvano il problema per iOS8 la prossima settimana, ma nel frattempo ...

La mia soluzione :

Alcuni hanno suggerito di utilizzare già Modernizr, beh Modernizr ti consente di creare i tuoi test. Quello che sto facendo fondamentalmente qui è 'astrarre' l'idea di un browser che supporti :hoverin un test Modernizr che posso usare in tutto il mio codice senza hardcoding if (iOS).

 Modernizr.addTest('workinghover', function ()
 {
      // Safari doesn't 'announce' to the world that it behaves badly with :hover
      // so we have to check the userAgent  
      return navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? false : true;
 });

Quindi il css diventa qualcosa del genere

html.workinghover .rollover:hover 
{
    // rollover css
}

Solo su iOS questo test fallirà e disabiliterà il rollover.

La parte migliore di tale astrazione è che se trovo che si rompe su un certo Android o se è stato risolto in iOS9, posso semplicemente modificare il test.


Questa soluzione è sia sgradevole che la migliore in circolazione. Si interrompe solo se un browser che ha problemi con il passaggio del mouse ha sia il tocco che il clic, ma per i browser solo tattili su ipad / iphone questo non è il caso. Il mio suggerimento è di implementarlo solo come correzione di ottimizzazione (per rimuovere lo sfarfallio del passaggio del mouse quando si utilizza già fastclick.js) e assicurarsi che tutto funzioni anche senza di esso.
Gersom

Grazie. Sono d'accordo. Curioso di vedere cosa fa iOS 9 per questo problema, semmai.
Simon_Weaver

Sono andato nella direzione opposta:html:not(.no-hover) .category:hover { ...
Chris Marisic

+1 per "Safari non" annuncia "al mondo che si comporta male con: hover" Questo problema rende iOS molto peggiore di qualsiasi versione di IE secondo me ...
Spencer O'Reilly

16

L'aggiunta della libreria FastClick alla tua pagina farà sì che tutti i tocchi su un dispositivo mobile vengano trasformati in eventi di clic (indipendentemente da dove l'utente fa clic), quindi dovrebbe anche risolvere il problema del passaggio del mouse sui dispositivi mobili. Ho modificato il tuo violino come esempio: http://jsfiddle.net/FvACN/8/ .

Basta includere la libreria fastclick.min.js sulla tua pagina e attivarla tramite:

FastClick.attach(document.body);

Come vantaggio collaterale, rimuoverà anche il fastidioso ritardo onClick di 300 ms di cui soffrono i dispositivi mobili.


Ci sono un paio di conseguenze minori nell'utilizzo di FastClick che possono o meno essere importanti per il tuo sito:

  1. Se tocchi un punto della pagina, scorri verso l'alto, scorri indietro verso il basso e poi rilasci il dito esattamente nella stessa posizione in cui lo avevi inizialmente posizionato, FastClick lo interpreterà come un "clic", anche se ovviamente non lo è. Almeno è così che funziona nella versione di FastClick che sto utilizzando (1.0.0). Qualcuno potrebbe aver risolto il problema dopo quella versione.
  2. FastClick rimuove la possibilità per qualcuno di "fare doppio clic".

1
Saresti disposto a condividere un altro esempio concreto di come posso farlo per il mio sito web. Non so nulla di javascript quindi sono un po 'confuso su come funzionerebbe. Ho tutti questi collegamenti sul mio sito Web su cui gli utenti di solito passano il mouse prima di fare clic, ma per ipad / mobile voglio che non debbano fare clic due volte.
Andy

@ Anddy - Non è esattamente chiaro di cosa hai bisogno e cosa ti manca ... Cosa hai provato finora e quali errori stai vedendo, se ce ne sono? Forse sarebbe meglio creare una nuova domanda Stackoverflow, se non l'hai già fatto, con un esempio di ciò che stai cercando di fare (?) O prova a chiarire cosa c'è nell'esempio sopra, jsfiddle che non fai ' t capire.
Troy

può essere utilizzato in applicazioni a pagina singola? Voglio dire, quando il contenuto DOM viene aggiornato ogni volta
Sebastien Lorber

Sì, lo sto usando con applicazioni a pagina singola.
Troy

16

Una soluzione migliore, senza alcun controllo JS, classe CSS e viewport: è possibile utilizzare Interaction Media Features (Media Queries Level 4)

Come questo:

@media (hover) {
  // properties
  my:hover {
    color: red;
  }
}

iOS Safari lo supporta

Maggiori informazioni su: https://www.jonathanfielding.com/an-introduction-to-interaction-media-features/


Non supportato da Firefox (al momento di questo commento ovviamente)
Frank

Questo è ora supportato anche da Firefox (FF 64+, rilasciato a dicembre 2018).
wunch

4

Esistono fondamentalmente tre scenari:

  1. L'utente ha solo un dispositivo mouse / puntatore e può attivarlo:hover
  2. L'utente ha solo un touchscreen e non può attivare gli :hoverelementi
  3. L'utente dispone sia di un touchscreen che di un dispositivo di puntamento

La risposta originariamente accettata funziona alla grande se sono possibili solo i primi due scenari, in cui un utente ha il puntatore o il touchscreen. Questo era comune quando l'OP ha posto la domanda 4 anni fa. Diversi utenti hanno sottolineato che i dispositivi Windows 8 e Surface rendono più probabile il terzo scenario.

La soluzione iOS al problema di non essere in grado di passare il mouse sui dispositivi touchscreen (come descritto in dettaglio da @Zenexer) è intelligente, ma può causare un comportamento anomalo del codice semplice (come notato dall'OP). Disabilitare il passaggio del mouse solo per i dispositivi touchscreen significa che sarà comunque necessario codificare un'alternativa compatibile con il touchscreen. Rilevare quando un utente ha sia il puntatore che il touchscreen confonde ulteriormente le acque (come spiegato da @Simon_Weaver).

A questo punto, la soluzione più sicura è evitare di utilizzare :hovercome unico modo in cui un utente può interagire con il tuo sito web. Gli effetti al passaggio del mouse sono un buon modo per indicare che un link o un pulsante è utilizzabile, ma a un utente non dovrebbe essere richiesto di passare il mouse su un elemento per eseguire un'azione sul tuo sito web.

Ripensare la funzionalità "hover" con i touchscreen in mente ha una buona discussione sugli approcci UX alternativi. Le soluzioni fornite dalla risposta includono:

  • Sostituzione dei menu al passaggio del mouse con azioni dirette (collegamenti sempre visibili)
  • Sostituzione dei menu al passaggio del mouse con menu al tocco
  • Spostamento di grandi quantità di contenuti al passaggio del mouse in una pagina separata

Andando avanti, questa sarà probabilmente la migliore soluzione per tutti i nuovi progetti. La risposta accettata è probabilmente la seconda migliore soluzione, ma assicurati di tenere conto dei dispositivi che hanno anche dispositivi di puntamento. Fai attenzione a non eliminare la funzionalità quando un dispositivo ha un touchscreen solo per aggirare l' :hoverhack di iOS .


1

La versione di JQuery nel tuo .css usa .no-touch .my-element: hover per tutte le tue regole di hover include JQuery e il seguente script

function removeHoverState(){
    $("body").removeClass("no-touch");
}

Quindi nel tag body aggiungi class = "no-touch" ontouchstart = "removeHoverState ()"

non appena l'ontouchstart attiva la classe per tutti gli stati hover viene rimossa


0

Invece di avere effetti al passaggio del mouse solo quando il tocco non è disponibile, ho creato un sistema per la gestione degli eventi di tocco e questo ha risolto il problema per me. Per prima cosa, ho definito un oggetto per testare gli eventi "tap" (equivalente a "click").

touchTester = 
{
    touchStarted: false
   ,moveLimit:    5
   ,moveCount:    null
   ,isSupported:  'ontouchend' in document

   ,isTap: function(event)
   {
      if (!this.isSupported) {
         return true;
      }

      switch (event.originalEvent.type) {
         case 'touchstart':
            this.touchStarted = true;
            this.moveCount    = 0;
            return false;
         case 'touchmove':
            this.moveCount++;
            this.touchStarted = (this.moveCount <= this.moveLimit);
            return false;
         case 'touchend':
            var isTap         = this.touchStarted;
            this.touchStarted = false;
            return isTap;
         default:
            return true;
      }
   }
};

Quindi, nel mio gestore di eventi, faccio qualcosa di simile al seguente:

$('#nav').on('click touchstart touchmove touchend', 'ul > li > a'
            ,function handleClick(event) {
               if (!touchTester.isTap(event)) {
                  return true;
               }

               // touch was click or touch equivalent
               // nromal handling goes here.
            });

0

Grazie @ Morgan Cheng per la risposta, tuttavia ho leggermente modificato la funzione JS per ottenere il " touchstart " (codice preso dalla risposta di @Timothy Perez ), tuttavia, per questo è necessario jQuery 1.7+

  $(document).on({ 'touchstart' : function(){
      //do whatever you want here
    } });

0

Data la risposta fornita da Zenexer, un pattern che non richiede tag HTML aggiuntivi è:

jQuery('a').on('mouseover', function(event) {
    event.preventDefault();
    // Show and hide your drop down nav or other elem
});
jQuery('a').on('click', function(event) {
    if (jQuery(event.target).children('.dropdown').is(':visible') {
        // Hide your dropdown nav here to unstick
    }
});

Questo metodo attiva prima il passaggio del mouse, poi il clic.


0

Sono d'accordo che disabilitare il passaggio del mouse per il tocco sia la strada da percorrere.

Tuttavia, per risparmiarti la fatica di riscrivere il tuo css, inserisci semplicemente tutti gli :hoverelementi @supports not (-webkit-overflow-scrolling: touch) {}

.hover, .hover-iOS {
  display:inline-block;
  font-family:arial;
  background:red;
  color:white;
  padding:5px;
}
.hover:hover {
  cursor:pointer;
  background:green;
}

.hover-iOS {
  background:grey;
}

@supports not (-webkit-overflow-scrolling: touch) {
  .hover-iOS:hover {
    cursor:pointer;
    background:blue;
  }

}
<input type="text" class="hover" placeholder="Hover over me" />

<input type="text" class="hover-iOS" placeholder="Hover over me (iOS)" />


0

Per quelli con un caso d'uso comune di disabilitazione degli :hovereventi su iOS Safari, il modo più semplice è utilizzare una media query di larghezza minima per i tuoi :hovereventi che rimane al di sopra della larghezza dello schermo dei dispositivi che stai evitando. Esempio:

@media only screen and (min-width: 1024px) {
  .my-div:hover { // will only work on devices larger than iOS touch-enabled devices. Will still work on touch-enabled PCs etc.
    background-color: red;
  }
}

-6

Basta guardare le dimensioni dello schermo ...

@media (min-width: 550px) {
    .menu ul li:hover > ul {
    display: block;
}
}

-12

ecco il codice in cui vorresti inserirlo

// a function to parse the user agent string; useful for 
// detecting lots of browsers, not just the iPad.
function checkUserAgent(vs) {
    var pattern = new RegExp(vs, 'i');
    return !!pattern.test(navigator.userAgent);
}
if ( checkUserAgent('iPad') ) {
    // iPad specific stuff here
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.