W mojej witrynie mam kilka menu css, które rozwijają się o :hover
(bez js)
Działa to w częściowo zepsuty sposób na iDevices, na przykład dotknięcie aktywuje :hover
regułę i rozszerzy menu, ale stuknięcie w innym miejscu nie spowoduje usunięcia :hover
. Również jeśli w elemencie, który jest :hover
'ed, znajduje się link , musisz dwukrotnie dotknąć, aby aktywować łącze (pierwsze :hover
dotknięcie wyzwala łącze, drugie dotknięcie wyzwala łącze).
Udało mi się sprawić, że wszystko działa dobrze na iPhonie, wiążąc touchstart
wydarzenie.
Problem polega na tym, że czasami mobilne safari nadal wybiera wyzwalanie :hover
reguły z css zamiast z moich touchstart
wydarzeń!
Wiem, że to jest problem, ponieważ kiedy wyłączam wszystkie :hover
reguły ręcznie w css, mobilne safari działa świetnie (ale zwykłe przeglądarki już oczywiście nie).
Czy istnieje sposób na dynamiczne „anulowanie” :hover
reguł dla niektórych elementów, gdy użytkownik korzysta z mobilnego safari?
Zobacz i porównaj zachowanie iOS tutaj: http://jsfiddle.net/74s35/3/ Uwaga: tylko niektóre właściwości css wyzwalają zachowanie dwóch kliknięć, np. Display: none; ale nie tło: czerwone; lub dekoracja tekstu: podkreślenie;
źródło
Odpowiedzi:
Zauważyłem, że „: hover” jest nieprzewidywalne w Safari na iPhonie / iPadzie. Czasami dotknięcie elementu powoduje, że element „: hover”, a czasami dryfuje do innych elementów.
Na razie mam po prostu lekcje bez dotykania ciała.
<body class="yui3-skin-sam no-touch"> ... </body>
I wszystkie reguły CSS mają z „: hover” poniżej „.no-touch”:
.no-touch my:hover{ color: red; }
Gdzieś na stronie mam javascript do usunięcia klasy bezdotykowej z ciała.
if ('ontouchstart' in document) { Y.one('body').removeClass('no-touch'); }
To nie wygląda idealnie, ale i tak działa.
źródło
:hover
to nie jest problem. Safari na iOS działa według bardzo dziwnej zasady. Strzelamouseover
imousemove
pierwszy; jeśli cokolwiek zostanie zmienione podczas tych wydarzeń, „kliknięcie” i powiązane zdarzenia nie zostaną uruchomione:mouseenter
imouseleave
wydają się być uwzględnione, chociaż nie są określone w wykresie.Jeśli w wyniku tych zdarzeń zmienisz cokolwiek, zdarzenia kliknięcia nie zostaną uruchomione. Obejmuje to coś wyżej w drzewie DOM. Na przykład zapobiegnie to działaniu pojedynczych kliknięć w Twojej witrynie za pomocą jQuery:
$(window).on('mousemove', function() { $('body').attr('rel', Math.random()); });
Edycja: Dla wyjaśnienia,
hover
zdarzenie jQuery zawieramouseenter
imouseleave
. Oba zapobiegnąclick
zmianie treści.źródło
hover
obsługi jquery uniemożliwiałclick
trafienie oddzielnego modułu obsługi - co za żart!mouseenter
jsbin.com/maseti/5/edit?html,js,outputModernizer biblioteki wykrywania funkcji przeglądarki obejmuje sprawdzanie zdarzeń dotykowych.
Domyślnym zachowaniem jest zastosowanie klas do elementu HTML dla każdej wykrytej funkcji. Następnie możesz użyć tych klas do stylizacji dokumentu.
Jeśli zdarzenia dotykowe nie są włączone, Modernizr może dodać klasę
no-touch
:<html class="no-touch">
Następnie określ zakres stylów najechania na tę klasę:
.no-touch a:hover { /* hover styles here */ }
Możesz pobrać niestandardową kompilację Modernizr, aby uwzględnić tak mało lub tyle funkcji wykrywania funkcji, ile potrzebujesz.
Oto przykład niektórych klas, które można zastosować:
<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">
źródło
Niektóre urządzenia (jak powiedzieli inni) obsługują zarówno zdarzenia dotyku, jak i myszy. Na przykład Microsoft Surface ma ekran dotykowy, gładzik ORAZ rysik, który faktycznie podnosi zdarzenia najechania kursorem, gdy znajduje się nad ekranem.
Każde rozwiązanie, które wyłącza się
:hover
na podstawie obecności zdarzeń „dotyku”, będzie miało również wpływ na użytkowników Surface (i wiele innych podobnych urządzeń). Wiele nowych laptopów jest dotykowych i reaguje na zdarzenia dotykowe - więc wyłączenie najechania kursorem jest naprawdę złą praktyką.To błąd w Safari, nie ma absolutnie żadnego uzasadnienia dla tego okropnego zachowania. Odmawiam sabotowania przeglądarek innych niż iOS z powodu błędu w iOS Safari, który najwyraźniej istnieje od lat. Naprawdę mam nadzieję, że naprawią to w iOS8 w przyszłym tygodniu, ale w międzyczasie ....
Moje rozwiązanie :
Niektórzy już sugerowali użycie Modernizra, no cóż, Modernizr pozwala na tworzenie własnych testów. To, co tutaj robię, to „abstrahowanie” idei przeglądarki, która obsługuje,
:hover
do testu Modernizr, którego mogę używać w całym kodzie bez kodowaniaif (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; });
Wtedy css staje się czymś takim
html.workinghover .rollover:hover { // rollover css }
Tylko w systemie iOS ten test zakończy się niepowodzeniem i wyłączy najazd.
Najlepszą częścią takiej abstrakcji jest to, że jeśli stwierdzę, że psuje się na pewnym Androidzie lub jeśli jest naprawiony w iOS9, mogę po prostu zmodyfikować test.
źródło
html:not(.no-hover) .category:hover { ...
Lepsze rozwiązanie, bez żadnej klasy JS, css i sprawdzania widoku: możesz korzystać z funkcji interakcji z mediami (zapytania o media poziom 4)
Lubię to:
@media (hover) { // properties my:hover { color: red; } }
Obsługuje to iOS Safari
Więcej informacji: https://www.jonathanfielding.com/an-introduction-to-interaction-media-features/
źródło
Dodanie biblioteki FastClick do strony spowoduje, że wszystkie dotknięcia urządzenia mobilnego zostaną zamienione w zdarzenia kliknięcia (niezależnie od tego, gdzie użytkownik kliknie), więc powinno również rozwiązać problem z najechaniem kursorem na urządzeniach mobilnych. Edytowałem twoje skrzypce jako przykład: http://jsfiddle.net/FvACN/8/ .
Po prostu umieść bibliotekę fastclick.min.js na swojej stronie i aktywuj przez:
FastClick.attach(document.body);
Dodatkową korzyścią jest również usunięcie irytującego opóźnienia onClick 300 ms, na które cierpią urządzenia mobilne.
Istnieje kilka drobnych konsekwencji korzystania z FastClick, które mogą, ale nie muszą mieć znaczenia dla Twojej witryny:
źródło
Zasadniczo istnieją trzy scenariusze:
:hover
:hover
elementówPierwotnie przyjęty odpowiedź działa świetnie, jeśli to możliwe są tylko dwa pierwsze scenariusze, gdzie użytkownik ma zarówno wskaźnik lub ekranu dotykowego. Było to powszechne, gdy PO zadał to pytanie 4 lata temu. Kilku użytkowników wskazało, że urządzenia z systemem Windows 8 i Surface zwiększają prawdopodobieństwo trzeciego scenariusza.
Rozwiązanie iOS problemu braku możliwości najechania kursorem na urządzenia z ekranem dotykowym (zgodnie z opisem @Zenexer) jest sprytne, ale może spowodować niewłaściwe zachowanie prostego kodu (jak zauważył OP). Wyłączenie wskaźnika tylko dla urządzeń z ekranem dotykowym oznacza, że nadal będziesz musiał kodować alternatywę przyjazną dla ekranu dotykowego. Wykrywanie, kiedy użytkownik ma zarówno wskaźnik, jak i ekran dotykowy, dodatkowo zagmatwa wody (zgodnie z wyjaśnieniem @Simon_Weaver).
W tym momencie najbezpieczniejszym rozwiązaniem jest unikanie używania
:hover
jako jedynego sposobu interakcji użytkownika z Twoją witryną. Efekty najechania kursorem to dobry sposób na wskazanie, że link lub przycisk jest aktywny, ale użytkownik nie powinien być zmuszony do najechania kursorem na element, aby wykonać akcję w Twojej witrynie.Ponowne przemyślenie funkcji „najechania kursorem” z myślą o ekranach dotykowych stanowi dobrą dyskusję na temat alternatywnych podejść do UX. Rozwiązania podane w odpowiedzi to:
Idąc dalej, będzie to prawdopodobnie najlepsze rozwiązanie dla wszystkich nowych projektów. Przyjęta odpowiedź jest prawdopodobnie drugim najlepszym rozwiązaniem, ale należy wziąć pod uwagę urządzenia, które również mają urządzenia wskazujące. Uważaj, aby nie wyeliminować funkcji, gdy urządzenie ma ekran dotykowy tylko po to, aby obejść
:hover
hack iOS .źródło
Wersja JQuery w twoim .css używa .no-touch .my-element: hover dla wszystkich reguł hover obejmuje JQuery i następujący skrypt
function removeHoverState(){ $("body").removeClass("no-touch"); }
Następnie w tagu body dodaj class = "no-touch" ontouchstart = "removeHoverState ()"
gdy tylko ontouchstart odpali, klasa dla wszystkich stanów najechania jest usuwana
źródło
Zamiast mieć efekty najechania tylko wtedy, gdy dotyk nie jest dostępny, stworzyłem system do obsługi zdarzeń dotykowych i to rozwiązało problem za mnie. Najpierw zdefiniowałem obiekt do testowania pod kątem zdarzeń „tap” (odpowiednik „kliknięcia”).
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; } } };
Następnie w moim module obsługi zdarzeń wykonuję coś takiego:
$('#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. });
źródło
Dzięki @Morgan Cheng za odpowiedź, jednak nieznacznie zmodyfikowałem funkcję JS, aby uzyskać " touchstart " (kod wzięty z odpowiedzi @Timothy Perez ), ale potrzebujesz do tego jQuery 1.7+
$(document).on({ 'touchstart' : function(){ //do whatever you want here } });
źródło
Biorąc pod uwagę odpowiedź dostarczoną przez Zenexer, wzorzec, który nie wymaga dodatkowych tagów HTML, to:
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 } });
Ta metoda uruchamia się najpierw po najechaniu kursorem myszy, a następnie po kliknięciu.
źródło
Zgadzam się, że najlepszym rozwiązaniem jest wyłączenie wskaźnika na dotyk.
Jednak, aby zaoszczędzić sobie kłopotu z ponownym pisaniem swojego css, po prostu zawiń wszystkie
:hover
elementy@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)" />
źródło
Dla osób z typowym przypadkiem wyłączania
:hover
zdarzeń w iOS Safari najprostszym sposobem jest użycie zapytania o media o minimalnej szerokości dla:hover
wydarzeń, które pozostaje powyżej szerokości ekranu urządzeń, których unikasz. Przykład:@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; } }
źródło
Spójrz tylko na rozmiar ekranu ...
@media (min-width: 550px) { .menu ul li:hover > ul { display: block; } }
źródło
oto kod, w którym chcesz go umieścić
// 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 }
źródło