36

My navigation is quite simple. I have a hover state which adds a border and a transparent gradient png background to some text, and an additional class which, when added by jQuery, adds color behind that transparent image.

If you click to toggle the class in a web browser you'll see the color come in and out, but the background image stays if you never move the mouse away from the button. This is expected behavior.

My problem is that when using an iPad, the touch seems to retain the hover state and the :hover properties never go away unless you click another button, in which case the persistent :hover properties are added to that button until another is pressed.

I can't imagine that I am the first with this problem, but searches haven't turned anything up.

Help?

enter image description here

Normal - Hover - Active (via addClass() )

d2burke
  • 4,081
  • 3
  • 39
  • 51
  • my only thought for now is to add a script that removes the CSS for the :hover property when viewing in Mobile Safari :( – d2burke May 17 '12 at 20:28

8 Answers8

20

If you're using Modernizr, the no-touch class will be added to the root html element for non-touch devices. Then you can do this:

a.myclass {
   color:#999;
}

.no-touch a.myclass:hover,
a.myclass:active {
   color:#ccc;
}
Redtopia
  • 4,947
  • 7
  • 45
  • 68
  • .no-touch is being depreciated from Modernizr because it is unreliable. All IE will give a false negative. – Jason Jul 13 '15 at 19:12
  • @Jason, do you have a link? I couldn't find any articles to verify. – Redtopia Jul 14 '15 at 19:32
  • see Paul Irish's comment here: https://github.com/Modernizr/Modernizr/issues/548#issuecomment-11968475 More reading: http://www.html5rocks.com/en/mobile/touchandmouse/ – Jason Jul 14 '15 at 23:47
16

Hover is a CSS pseudo class designed to work with a pointer not so much with touch events. You would normally want to avoid the use of hover altogether since it makes no sense on mobile. One of the easiest ways to deal with this is by placing your hover CSS inside a media query. You can do this by targeting tablets or desktop screens, here are the two solutions:

1- Targeting iPads:

@media only screen 
and (min-device-width : 768px) 
and (max-device-width : 1024px) {
    RE.no-touch .my-element:hover {
         /* in here you cancel all hover styles you applied for desktop */ 
    }
}

2- Targeting desktops:

@media only screen 
and (min-width : 1224px) {
    RE.no-touch .my-element:hover {
         /* in here you apply hover styles that will only work on desktop */ 
    }
}

Either way is valid. You can change the values of min and max width if you notice that the CSS is being triggered incorrectly in certain devices.

charlitosJS
  • 294
  • 2
  • 10
  • This is a great idea. Make sure you don't remove it for all users or you will make it hard for seeing impaired and mobility impaired users. This does that nicely. – Jake Jul 01 '14 at 23:20
  • 6
    Device width is increasingly becoming an irrelevant factor for distinguishing touch devices from pointer input devices. Is there another media query that could be used? – Adam Grant Jan 08 '15 at 16:10
  • Good tip, solved my iOS hover issue. But I think that the desktop query should use `min-device-width`, so it will still work on desktops with reduced browser windows. – terrymorse Apr 01 '16 at 16:05
9

This is an old post but I was struggling with this today in 2019, and I find this clean solution and I want to share with all of you.

<a href="#">Try hovering over me!</a>

@media (any-hover: hover) {
  a:hover {
    background: yellow;
  }
}

Basically what are we doing here is using hover in any device with pointer mechanism. Nowadays there is softwares with html readers for a better accessibility of the site and should not be a good idea, deactivate :hover for all mobile devices. Just to be clear, any device without pointing mechanism the hover will not take effect, otherwise we applied the :hover style.

Kenny Hank
  • 91
  • 1
  • 2
  • Very clean and fixed my issue with hover, focus and styling on touch devices. The media query cannot include additional qualifiers like `only screen`, but has to be as shown in answer above. – Johan Oct 31 '22 at 09:26
3

This is how I fixed it for my list with a hover:

CSS:

ul {background-color:#f3f3f3;}
li:hover {background-color: #e7e7e7}

jQuery:

$(document).ready(function () {
    $('li').on('touchstart', function () { $(this).css('background-color', ''); });
    $('li').on('touchend', function () { $(this).css('background-color', 'inherit'); });
});

And a fix for hover on back buton navigation...

<body onunload="$('a').blur();">

or

$(window).unload( function () { $('a').blur(); } );

For the unini­ti­ated, the unload event occurs right before the browser leaves the page. By blur­ring all of the links in the page on unload, I was able to un-stick the hover state. Returning to the orig­inal page using the back button shows the clicked link in the default state.

Unfortunately, an onclick event doesn't seem to trigger the unload, but a refresh does in iOS 5?!

Stuck On :hover - http://www.aaronpinero.com/articulated/2011/05/28/stuck-on-hover/ Use jQuery to replace - http://www.nerdboy.co.uk/2009/01/use-jquery-to-replace-body-onunload/

Zymotik
  • 6,412
  • 3
  • 39
  • 48
2

I ran across this issue as well. My problem was on the iPad, if I tapped an anchor and scrolled slightly, a click event did not fire but the element would retain the hover state CSS. My belief is that the iPad was actually retaining a focus state and applying the :hover CSS styles in an effort to simulate the desktop experience.

A non-JS and practical workaround is to not deliver hover styles to touch devices if there is not a need for them. Touch devices do not have a "real" hover state, however it appears hover is simulated when an element is focused or tapped-and-scrolled upon.

So with Modernizr.js loaded, I moved my hover styles into the scope of .no-touch -

i.e.

/* Only deliver hover styles to non-touch devices */
.no-touch .my-element:hover {
  /* hover styles */
}

Then, when the element is clicked I am toggling an active class on it.

.my-element.active {
  /* active styles */
}

The only negative takeaway is you do not get a "being tapped state". I have yet to investigate a solution for that as it's not really important for my apps.

BradGreens
  • 1,357
  • 4
  • 15
  • 31
1

you need to catch the following events

touchstart - when your finger touches the screen

touchend - when your finger leaves the screen

think of as hover and unhover, if you can post some code we can help you more if you don't understand what I am saying

$('element').on('touchstart', function(){
    // apply hover styles, or better yet addClass() of hover styles
});

$('element').on('touchend', function(){
    // removeClass or remove styles, in here you set it to default so even if it is focused, it makes no difference
});

Give some sample code, the touchstart and end should solve the problem

Huangism
  • 16,278
  • 7
  • 48
  • 74
  • Thanks Huangism, but from here i'm not sure what to do to get the link to lose focus. .blur() doesn't cut it like it does with form controls. – d2burke May 17 '12 at 21:01
  • I had already amended the 'touchstart' code to 'touchend' and it doesn't work. i can definitely get event to fire on touchend (thanks for this, by the way, i'll use it somewhere else even if this doesn't work), but it won't cause the link to lose focus – d2burke May 17 '12 at 21:14
0

If you are using SCSS, I recommend you do the following:

// primary-buttons
.primary-button, .primary-button:visited,
.primary-button:focus {
    background: red;
    color: white;
}

.primary-button:hover {
    background: green; // Only for big screens
    @media(max-width: 768px) {
        background: red;
    }
}

If you reset the value of the background to the initial value, you will no longer have problems with the button being pressed on devices <= 768px resolution

juanjinario
  • 596
  • 1
  • 9
  • 24
-5

This is a workaround:

var btn = jQuery(".nav-menu ul li:first");
var btn_new = btn.clone(true);
btn.remove();
jQuery(".nav-menu ul").append(btn_new);
debain
  • 1