Ist es möglich, das Ignorieren der: hover-Pseudoklasse für iPhone / iPad-Benutzer zu erzwingen?

94

Ich habe einige CSS-Menüs auf meiner Website, die mit :hover(ohne js) erweitert werden.

Dies funktioniert auf iDevices halb unterbrochen. Wenn Sie beispielsweise auf tippen, wird die :hoverRegel aktiviert und das Menü erweitert. Wenn Sie jedoch auf eine andere Stelle tippen, wird das nicht entfernt :hover. Auch wenn sich innerhalb des Elements ein Link befindet :hover, der bearbeitet wird, müssen Sie zweimal tippen, um den Link zu aktivieren (erster Tap-Trigger :hover, zweiter Tap-Trigger-Link).

Ich habe es geschafft, dass die Dinge auf dem iPhone gut funktionieren, indem ich das touchstartEreignis gebunden habe .

Das Problem ist, dass mobile Safari manchmal immer noch die :hoverRegel aus dem CSS anstelle meiner touchstartEreignisse auslöst !

Ich weiß, dass dies das Problem ist, denn wenn ich alle :hoverRegeln manuell im CSS deaktiviere , funktioniert die mobile Safari hervorragend (aber normale Browser funktionieren offensichtlich nicht mehr).

Gibt es eine Möglichkeit, :hoverRegeln für bestimmte Elemente dynamisch "abzubrechen" , wenn sich der Benutzer auf einer mobilen Safari befindet?

Sehen und vergleichen Sie das iOS-Verhalten hier: http://jsfiddle.net/74s35/3/ Hinweis: Nur einige CSS-Eigenschaften lösen das Zwei-Klick-Verhalten aus, z. B. Anzeige: keine; aber nicht Hintergrund: rot; oder Textdekoration: unterstreichen;

Christopher Camps
quelle
7
BITTE deaktivieren Sie das Schweben NICHT, nur weil Berührungsereignisse vorhanden sind. Dies wirkt sich nachteilig auf Benutzer von Laptops mit Touch- und Mauseingabegeräten aus. Siehe meine Antwort für weitere Details
Simon_Weaver

Antworten:

77

Ich fand, dass ": hover" in iPhone / iPad Safari nicht vorhersehbar ist. Tippen Sie manchmal auf ein Element, um dieses Element ": hover" zu machen, während es manchmal zu anderen Elementen wechselt.

Zur Zeit habe ich nur eine "No-Touch" -Klasse am Körper.

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

Und haben Sie alle CSS-Regeln mit ": hover" unter ".no-touch":

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

Irgendwo auf der Seite habe ich Javascript, um die berührungslose Klasse aus dem Körper zu entfernen .

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

Das sieht nicht perfekt aus, funktioniert aber trotzdem.

Morgan Cheng
quelle
5
Dies ist der einzige Weg, dies zu tun. Was mich stört ist, dass es die "normale" Seite mit Dingen verschmutzt, die nicht dorthin gehören und irrelevant sind. Naja. Vielen Dank.
Christopher Camps
Sie können auch Medienabfragen mit einem Fallback für IE
Lime
2
@Shackrock: Ich denke nicht, dass der Ansatz "ein Tippen zum Schweben und zwei zum Klicken" eine gute Lösung ist. Da es auf einem Touch-Gerät tatsächlich keinen tatsächlichen Schwebeflug gibt (bis sie mit Bildschirmen ausgestattet sind, auf denen Ihr Finger darüber schwebt), ist es meiner Meinung nach besser, das Schweben überhaupt nicht zu simulieren. Überspringen Sie bei Effekten, wie sie bei Schaltflächen und Links üblich sind, einfach den Effekt. Lassen Sie Menüs, die beim Hover erweitert werden, stattdessen beim Tippen / Klicken erweitert werden. Mein 2c.
Adrian Schmidt
18
Ich habe kürzlich Probleme bei dieser Art von Ansatz aufgrund neuer Windows 8-Systeme mit Touch- und Mauseingabe gefunden
Tom
4
+1 zu Toms Kommentar - Die Überprüfung auf ontouchstart wird auf berührungsfähigen Laptops auch dann als wahr zurückgegeben, wenn sie an einen externen Monitor angeschlossen sind (wo eine mausfreundliche Site mit Schwebezuständen wünschenswerter ist).
Gregdev
45

:hoverist hier nicht das Problem. Safari für iOS folgt einer sehr merkwürdigen Regel. Es feuert mouseoverund mousemovezuerst; Wenn sich während dieser Ereignisse etwas ändert, werden 'Klick' und verwandte Ereignisse nicht ausgelöst:

Diagramm des Berührungsereignisses in iOS

mouseenter und mouseleave scheinen enthalten zu sein, obwohl sie nicht in der Tabelle angegeben sind.

Wenn Sie aufgrund dieser Ereignisse Änderungen vornehmen, werden Klickereignisse nicht ausgelöst. Dazu gehört etwas weiter oben im DOM-Baum. Dies verhindert beispielsweise, dass einzelne Klicks mit jQuery auf Ihrer Website funktionieren:

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

Bearbeiten: Zur Verdeutlichung enthält jQuerys hoverEreignis mouseenterund mouseleave. Beides verhindert, clickdass Inhalte geändert werden.

Zenexer
quelle
5
Natürlich: Schweben ist das Problem :-) oder besser gesagt Safaris fehlerhafte Implementierung. Dies ist ein interessanter Hintergrund zu diesem Problem, aber Apple ist die einzige Partei, die dieses schreckliche Verhalten beheben kann. Entweder muss es "ausgeblendet" werden, wenn auf etwas außerhalb der Elemente geklickt wird, oder es muss überhaupt nicht "schweben". Das aktuelle Verhalten bricht grundsätzlich jede Website, die verwendet: Hover für eine Maus
Simon_Weaver
Dies half mir zu erkennen, dass der JQuery- hoverHandler verhinderte, dass ein separater clickHandler getroffen wurde - was für ein Witz!
Simon_Weaver
1
Geniale Antwort und beste Erklärung für das Problem, auf das ich gestoßen bin. Es sind nicht nur CSS-Hovers, die das "Doppelklick" -Problem verursachen, sondern buchstäblich jede DOM-Änderung nach den Ereignissen von mouseover / mouseenter et al.
Oliver Joseph Ash
Hier ist ein Beispiel, das zeigt, dass das mouseenter Klickereignis
Oliver Joseph Ash
@ Oliver Joseph Ash Richtig, daher das JS-Beispiel in der Antwort. ;)
Zenexer
18

Die Browser-Feature-Erkennungsbibliothek Modernizer enthält eine Überprüfung auf Berührungsereignisse.

Standardmäßig werden für jedes erkannte Feature Klassen auf Ihr HTML-Element angewendet. Sie können diese Klassen dann verwenden, um Ihr Dokument zu formatieren.

Wenn Touch-Ereignisse nicht aktiviert sind, kann Modernizr eine Klasse hinzufügen no-touch:

<html class="no-touch">

Und dann erweitern Sie Ihre Schwebestile mit dieser Klasse:

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

Sie können einen benutzerdefinierten Modernizr-Build herunterladen der so wenige oder so viele Funktionserkennungen enthält, wie Sie benötigen.

Hier ist ein Beispiel für einige Klassen, die angewendet werden können:

<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">
Beau Smith
quelle
Beachten Sie, dass Modernizr in neueren Versionen von Chrome auf Desktops "touch" meldet. Dies wird anscheinend in Version 3 mit Touchevents korrigiert.
Craig Jacobs
18

Einige Geräte (wie andere bereits gesagt haben) haben sowohl Berührungs- als auch Mausereignisse. Das Microsoft Surface verfügt beispielsweise über einen Touchscreen, ein Trackpad UND einen Stift, der tatsächlich Schwebeereignisse auslöst, wenn er über dem Bildschirm bewegt wird.

Jede deaktivierende Lösung :hover aufgrund von Berührungsereignissen sich auch auf Surface-Benutzer (und viele andere ähnliche Geräte) aus. Viele neue Laptops sind berührungsempfindlich und reagieren auf Berührungsereignisse. Daher ist das Deaktivieren des Schwebens eine sehr schlechte Vorgehensweise.

Dies ist ein Fehler in Safari, es gibt absolut keine Rechtfertigung für dieses schreckliche Verhalten. Ich weigere mich, Nicht-iOS-Browser wegen eines Fehlers in iOS Safari zu sabotieren, der anscheinend seit Jahren vorhanden ist. Ich hoffe wirklich, dass sie dies nächste Woche für iOS8 beheben, aber in der Zwischenzeit ...

Meine Lösung :

Einige haben bereits vorgeschlagen, Modernizr zu verwenden. Mit Modernizr können Sie Ihre eigenen Tests erstellen. Grundsätzlich abstrahiere ich hier die Idee eines Browsers, der :hovereinen Modernizr-Test unterstützt, den ich im gesamten Code ohne Hardcodierung verwenden kann 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;
 });

Dann wird das CSS so etwas

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

Nur unter iOS schlägt dieser Test fehl und deaktiviert den Rollover.

Der beste Teil einer solchen Abstraktion ist, dass ich den Test einfach ändern kann, wenn ich finde, dass er auf einem bestimmten Android kaputt geht oder wenn er in iOS9 behoben ist.

Simon_Weaver
quelle
Diese Lösung ist sowohl böse als auch die beste da draußen. Es bricht nur ab, wenn ein Browser, der mit dem Schwebeflug zu kämpfen hat, sowohl Berührung als auch Klick hat. Bei den Nur-Berührungs-Browsern auf dem iPad / iPhone ist dies jedoch nicht der Fall. Mein Vorschlag ist, dies nur als Optimierungsfix zu implementieren (um Schwebeflackern zu entfernen, wenn bereits fastclick.js verwendet wird) und sicherzustellen, dass alles auch ohne dieses Problem funktioniert.
Gersom
Vielen Dank. Genau. Neugierig zu sehen, was iOS 9 für dieses Problem tut - wenn überhaupt.
Simon_Weaver
Ich ging in die entgegengesetzte Richtung:html:not(.no-hover) .category:hover { ...
Chris Marisic
+1 für "Safari kündigt der Welt nicht an, dass es sich schlecht verhält mit: hover" Dieses Problem macht iOS meiner Meinung nach viel schlimmer als jede andere Version von IE ...
Spencer O'Reilly
16

Durch Hinzufügen der FastClick-Bibliothek zu Ihrer Seite werden alle Taps auf einem mobilen Gerät in Klickereignisse umgewandelt (unabhängig davon, wo der Benutzer klickt). Daher sollte das Hover-Problem auch auf mobilen Geräten behoben werden. Ich habe Ihre Geige als Beispiel bearbeitet: http://jsfiddle.net/FvACN/8/ .

Fügen Sie einfach die Bibliothek fastclick.min.js in Ihre Seite ein und aktivieren Sie sie über:

FastClick.attach(document.body);

Als Nebeneffekt wird auch die lästige onClick-Verzögerung von 300 ms beseitigt, unter der mobile Geräte leiden.


Die Verwendung von FastClick hat einige geringfügige Konsequenzen, die für Ihre Website möglicherweise von Bedeutung sind oder nicht:

  1. Wenn Sie irgendwo auf der Seite tippen, nach oben scrollen, wieder nach unten scrollen und dann Ihren Finger genau an der Position loslassen, an der Sie ihn ursprünglich platziert haben, interpretiert FastClick dies als "Klick", obwohl dies offensichtlich nicht der Fall ist. Zumindest funktioniert das in der Version von FastClick, die ich derzeit verwende (1.0.0). Möglicherweise hat jemand das Problem seit dieser Version behoben.
  2. FastClick entfernt die Möglichkeit für jemanden, "doppelt zu klicken".
Troy
quelle
1
Möchten Sie ein weiteres konkretes Beispiel dafür nennen, wie ich dies für meine Website tun kann? Ich weiß nichts über Javascript, daher bin ich etwas verwirrt darüber, wie das funktionieren würde. Ich habe all diese Links auf meiner Website, über die Benutzer normalerweise vor dem Klicken den Mauszeiger bewegen, aber für iPad / Mobile möchte ich sie so gestalten, dass sie nicht zweimal klicken müssen.
Andy
@Andy - Es ist nicht genau klar, was Sie brauchen und was Sie vermissen ... Was haben Sie bisher versucht und welche Fehler sehen Sie, wenn überhaupt? Vielleicht ist es am besten, eine neue Stackoverflow-Frage zu erstellen, wenn Sie dies noch nicht getan haben, mit einem Beispiel dafür, was Sie versuchen (?) Oder zu klären, was im obigen Beispiel jsfiddle steht, das Sie nicht verwenden. ich verstehe nicht.
Troy
Kann dies in einseitigen Anwendungen verwendet werden? Ich meine, wenn DOM-Inhalte jedes Mal aktualisiert werden
Sebastien Lorber
Ja, ich verwende es mit einseitigen Anwendungen.
Troy
16

Eine bessere Lösung ohne JS-, CSS-Klassen- und Ansichtsfensterprüfung: Sie können Interaction Media Features (Media Queries Level 4) verwenden.

So was:

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

iOS Safari unterstützt es

Weitere Informationen zu: https://www.jonathanfielding.com/an-introduction-to-interaction-media-features/

Estevão Lucas
quelle
Von Firefox jedoch nicht unterstützt (zum Zeitpunkt dieses Kommentars natürlich)
Frank
Dies wird jetzt auch von Firefox unterstützt (FF 64+, veröffentlicht im Dezember 2018).
Wunch
4

Grundsätzlich gibt es drei Szenarien:

  1. Der Benutzer hat nur ein Maus- / Zeigergerät und kann es aktivieren:hover
  2. Der Benutzer hat nur einen Touchscreen und kann keine :hoverElemente aktivieren
  3. Der Benutzer hat sowohl einen Touchscreen als auch ein Zeigergerät

Die ursprünglich akzeptierte Antwort funktioniert hervorragend, wenn nur die ersten beiden Szenarien möglich sind, in denen ein Benutzer entweder einen Zeiger oder einen Touchscreen hat. Dies war üblich, als das OP die Frage vor 4 Jahren stellte. Mehrere Benutzer haben darauf hingewiesen, dass Windows 8- und Surface-Geräte das dritte Szenario wahrscheinlicher machen.

Die iOS-Lösung für das Problem, nicht auf Touchscreen-Geräten schweben zu können (wie von @Zenexer beschrieben), ist clever, kann jedoch dazu führen, dass sich einfacher Code schlecht verhält (wie vom OP angegeben). Wenn Sie den Hover nur für Touchscreen-Geräte deaktivieren, müssen Sie weiterhin eine Touchscreen-freundliche Alternative codieren. Das Erkennen, wenn ein Benutzer sowohl einen Zeiger als auch einen Touchscreen hat, trübt das Wasser weiter (wie von @Simon_Weaver erläutert).

Zu diesem Zeitpunkt besteht die sicherste Lösung darin, die Verwendung :hoverals einzige Möglichkeit zu vermeiden, mit der ein Benutzer mit Ihrer Website interagieren kann. Hover-Effekte sind eine gute Möglichkeit, um anzuzeigen, dass ein Link oder eine Schaltfläche umsetzbar ist. Ein Benutzer sollte jedoch nicht verpflichtet sein, ein Element zu bewegen, um eine Aktion auf Ihrer Website auszuführen.

Das Überdenken der Hover-Funktionalität unter Berücksichtigung von Touchscreens bietet eine gute Diskussion über alternative UX-Ansätze. Die Lösungen, die die Antwort dort bietet, umfassen:

  • Hover-Menüs durch direkte Aktionen ersetzen (immer sichtbare Links)
  • Ersetzen von On-Hover-Menüs durch On-Tap-Menüs
  • Verschieben großer Mengen von On-Hover-Inhalten auf eine separate Seite

In Zukunft wird dies wahrscheinlich die beste Lösung für alle neuen Projekte sein. Die akzeptierte Antwort ist wahrscheinlich die zweitbeste Lösung. Achten Sie jedoch darauf, Geräte zu berücksichtigen, die auch Zeigergeräte haben. Achten Sie darauf, die Funktionalität nicht zu beeinträchtigen, wenn ein Gerät über einen Touchscreen verfügt, um den :hoverHack von iOS zu umgehen.

Thirdender
quelle
1

Die JQuery-Version in Ihrem CSS verwendet .no-touch .my-element: hover für alle Ihre Hover-Regeln enthält JQuery und das folgende Skript

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

Fügen Sie dann im Body-Tag class = "no-touch" ontouchstart = "removeHoverState ()" hinzu.

Sobald der ontouchstart ausgelöst wird, wird die Klasse für alle Schwebezustände entfernt

Greg
quelle
0

Anstatt nur Schwebeeffekte zu haben, wenn keine Berührung verfügbar ist, habe ich ein System zur Behandlung von Berührungsereignissen erstellt, das das Problem für mich gelöst hat. Zuerst habe ich ein Objekt zum Testen auf "Tap" -Ereignisse (entspricht "Klick") definiert.

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;
      }
   }
};

Dann mache ich in meinem Event-Handler so etwas wie das Folgende:

$('#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.
            });
Krapfen
quelle
0

Vielen Dank an @Morgan Cheng für die Antwort. Ich habe jedoch die JS-Funktion leicht geändert, um den " Touchstart " (Code aus der Antwort von @Timothy Perez ) zu erhalten. Dafür benötigen Sie jedoch jQuery 1.7+

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

Angesichts der Antwort von Zenexer lautet ein Muster, für das keine zusätzlichen HTML-Tags erforderlich sind:

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
    }
});

Diese Methode löst zuerst den Mouseover und dann den Klick aus.

sehrphatisch
quelle
0

Ich bin damit einverstanden, dass das Deaktivieren von Hover for Touch der richtige Weg ist.

Um sich jedoch die Mühe zu ersparen, Ihr CSS neu zu schreiben, wickeln Sie einfach alle :hoverElemente ein @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)" />

Jordan Young
quelle
0

Für Benutzer mit häufigem Anwendungsfall zum Deaktivieren von :hoverEreignissen in iOS Safari besteht die einfachste Möglichkeit darin, eine Medienabfrage mit minimaler Breite für Ihre :hoverEreignisse zu verwenden, die über der Bildschirmbreite der Geräte liegt, die Sie vermeiden. Beispiel:

@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;
  }
}
Brian Scramlin
quelle
-6

Schauen Sie sich einfach die Bildschirmgröße an ....

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

Hier ist der Code, in den Sie ihn einfügen möchten

// 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
}
Luke
quelle