Is it possible to force ignore the :hover pseudoclass for iPhone/iPad users?

I have some css menus on my site that expand with :hover (without js)

This works in a semi-broken way on iDevices, for example a tap will activate the :hover rule and expand the menu, but then tapping elsewhere doesn't remove the :hover. Also if there is a link inside the element that is :hover'ed, you have to tap twice to activate the link (first tap triggers :hover, second tap triggers link).

I've been able to make things work nicely on iphone by binding the touchstart event.

The problem is that sometimes mobile safari still chooses to trigger the :hover rule from the css instead of my touchstart events!

I know this is the problem because when I disable all the :hover rules manually in the css, mobile safari works great (but regular browsers obviously don't anymore).

Is there a way to dynamically "cancel" :hover rules for certain elements when the user is on mobile safari?

See and compare iOS behavior here: http://jsfiddle.net/74s35/3/ Note: that only some css properties trigger the two-click behavior, e.g. display:none; but not background: red; or text-decoration: underline;


Solution 1:

I found that ":hover" is unpredictable in iPhone/iPad Safari. Sometimes tap on element make that element ":hover", while sometimes it drifts to other elements.

For the time being, I just have a "no-touch" class at body.

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

And have all CSS rules with ":hover" below ".no-touch":

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

Somewhere in the page, I have javascript to remove no-touch class from body.

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

This doesn't look perfect, but it works anyway.

Solution 2:

:hover isn't the issue here. Safari for iOS follows a very odd rule. It fires mouseover and mousemove first; if anything is changed during these events, 'click' and related events don't get fired:

Diagram of touch event in iOS

mouseenter and mouseleave appear to be included, though they're not specified in the chart.

If you modify anything as a result of these events, click events won't get fired. That includes something higher up in the DOM tree. For example, this will prevent single clicks from working on your website with jQuery:

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

Edit: For clarification, jQuery's hover event includes mouseenter and mouseleave. These will both prevent click if content is changed.

Solution 3:

A better solution, without any JS, css class and viewport check: you can use Interaction Media Features (Media Queries Level 4)

Like this:

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

iOS Safari supports it

More about: https://www.jonathanfielding.com/an-introduction-to-interaction-media-features/

Solution 4:

The browser feature detection library Modernizer includes a check for touch events.

It’s default behavior is to apply classes to your html element for each feature being detected. You can then use these classes to style your document.

If touch events are not enabled Modernizr can add a class of no-touch:

<html class="no-touch">

And then scope your hover styles with this class:

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

You can download a custom Modernizr build to include as few or as many feature detections as you need.

Here's an example of some classes that may be applied:

<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">