Links with mouseover (or hover) styles on touch devices are a bit of a dilemma. In short: they don’t really exist on these devices. Creating fancy :hover
styles can really add to the browser experience and help simplify your layout, but they simply will not work on a touch device. When a tablet or smartphone user taps your hover-styled link elements, the hover style shortly appears, and immediately the underlying link is followed/activated.
So how do we handle this problem? Do we sniff out touch devices and disable hover classes? Not at all: it can be fixed using JavaScript (specifically jQuery in this case), and with some minimal adjustments you can keep using your hover styles across all browsing devices. Here’s how:
The goal
When a touch user taps link A, only the hover style for link A is activated but the link does not open. After this, there are two possible options:
- User taps link A again -> link A is opened.
- User taps link B -> mouseover for link B is activated and the hover style for link A is deactivated.
Another thing to keep in mind is that if a user taps link A, then taps link B and then taps link A again, it should not consider link A as being tapped for a second time (which would lead to the browser to open the link).
What we need to accomplish
To realize our goal, we need to accomplish the following using JavaScript (jQuery)
- User taps the ‘tappable hover’ link
- Does the link have a
hover
class? - If it does, allow the click to proceed (return true)
- If it does not, add the hover class, remove it from all other tappable hover links, and prevent the click from continuing (preventDefault)
That’s it.
The (example) HTML
Specify a class for the link elements you want to enable hover for, as we will use this class in the jQuery selector (in this case taphover
).
<a class="taphover" href="">Link 1</a> <a class="taphover" href="">Link 2</a> <a class="taphover" href="">Link 3</a>
The CSS
Make sure you specify both pseudo (:hover) and class (.hover) for the link, so that they appear the same on both a touch device and regular browser:
a.taphover:hover, a.taphover.hover { // css for hovering here };
jQuery
To target just touch devices, I use the touchstart
event on the links to bind to.*
$('a.taphover').on('touchstart', function (e) { 'use strict'; //satisfy code inspectors var link = $(this); //preselect the link if (link.hasClass('hover')) { return true; } else { link.addClass('hover'); $('a.taphover').not(this).removeClass('hover'); e.preventDefault(); return false; //extra, and to make sure the function has consistent return points } });
View/fork on Github: https://gist.github.com/c-kick/31dfbfe7b948c16d17c8
That’s it, this should do he trick.
Demo
I’ve set up a working Fiddle here. Visit it using a touch device, or using debug tools like Chrome’s web inspector, which can simulate touch.
Note: the script will traverse the entire DOM, looking for .taphover links on which to remove the hover class. This is potentially inefficient, so adjust it as you seem fit. I suggest using a selector context for optimizing performance if needed.
* Note that binding to the touchstart
event, and setting preventDefault()
in the process will cause any other touches on the element(s) to fail. This means that if a user starts swiping/scrolling, starting on one of these links would disable the swipe. A work-around is to sniff out touch devices with something like Modernizr, and then binding to the regular click
event.
Hello, I came across your website and found your fix for smartphone mouseover and hover fix.
i was wondering if you could explain a little how I can implement this into my website, tipngreen.com
I have six mouseover div containers on the home page I would like to also be able to effectively execute on a smartphone. Any all help is greatly appreciated!! Thanks in advance
Great, thanks! Worked like a charm.
Thanks for your script, works well.
Is it possible to remove the hover style after the second click ?
In my case, i have a gallery of items. First click on the item shows the title of it and second click open a full screen modal. When I close the modal, the item is still styled with hover css.
Here is a link to my website (work in progress) : http://www.gvk.design (I apply your solution on the first item in the gallery).
Hi. I’m trying to create a div with a heading and icon, then use your (brilliant) script to swap out the content of the div with some descriptive text on hover/tap. Am trying all kinds of combinations to make it happen but just isn’t working. Can you suggest a way to set this up, using your script, so that it might work? Thanks very much.
Thanks so much for this brilliant piece of code. I’m developing my own site and it’s just what I need (and what others said was impossible). I’m having trouble getting it to work on my WordPress site though, so any advice you’d have would be MUCH appreciated.
I’m an amateur so… that being said, I’m trying to get this to work for several classes on my masonry portfolio. I tried replacing the classes in the code and patching the snippet into scripts.js, but it only alternately did nothing or crashed the site. My jQuery is nil, so it’s possible I have a syntax error, or putting it in the wrong place in scripts.js or perhaps it shouldn’t be there at all.
Please any advice would be so helpful. Thanks again for sharing this sweet patch.
Here is what I tried:
$(‘a.item-overlay’).on(“touchstart”, function (e) {
“use strict”; //satisfy the code inspectors
var link = $(this); //preselect the link
if (link.hasClass(‘hover’)) {
return true;
} else {
link.addClass(“hover”);
$(‘a.taphover’).not(this).removeClass(“hover”);
e.preventDefault();
return false; //extra, and to make sure the function has consistent return points
}
});
$(‘a.item-content’).on(“touchstart”, function (e) {
“use strict”; //satisfy the code inspectors
var link = $(this); //preselect the link
if (link.hasClass(‘hover’)) {
return true;
} else {
link.addClass(“hover”);
$(‘a.taphover’).not(this).removeClass(“hover”);
e.preventDefault();
return false; //extra, and to make sure the function has consistent return points
}
});
$(‘a.item-title’).on(“touchstart”, function (e) {
“use strict”; //satisfy the code inspectors
var link = $(this); //preselect the link
if (link.hasClass(‘hover’)) {
return true;
} else {
link.addClass(“hover”);
$(‘a.taphover’).not(this).removeClass(“hover”);
e.preventDefault();
return false; //extra, and to make sure the function has consistent return points
}
});
$(‘a.item-cat’).on(“touchstart”, function (e) {
“use strict”; //satisfy the code inspectors
var link = $(this); //preselect the link
if (link.hasClass(‘hover’)) {
return true;
} else {
link.addClass(“hover”);
$(‘a.taphover’).not(this).removeClass(“hover”);
e.preventDefault();
return false; //extra, and to make sure the function has consistent return points
}
});
The code looks fine, although I strongly suggest to keep it down to 1 script, that targets several links, instead of multiple instances of the script, for each link class (as you did now). Just follow my instructions to the letter and you’ll be fine.
Syntax errors will cause the browser to crash, so check your web console. Which browser do you use? Make sure you enable developer tools in it, so you can view error messages. Without any errors
Thanks for your reply! This site is mostly viewed on Safari. I took your advice but seem to still have a problem. As it’s a WordPress site I put the script in the footer. And get an invalid character u8216 syntax error on the first line… Here’s what I put in. Any thoughts or advice which be much appreciated!
$(‘a.item-overlay, a.item-title, a.item-cat’).on(“touchstart”, function (e) {
“use strict”; //satisfy the code inspectors
var link = $(this); //preselect the link
if (link.hasClass(‘hover’)) {
return true;
} else {
link.addClass(“hover”);
$(‘a.item-overlay, a.item-title, a.item-cat’).not(this).removeClass(“hover”);
e.preventDefault();
return false; //extra, and to make sure the function has consistent return points
}
});
This method is working for android well, but for IE (last version for may 2016) on mobile is not working, I click on the link1, link2, link3 and it does not stay red, it is quickly change color, red > gray > then it unselects (white again), and I could not make him stay in red bg. Any suggestions?
Have you checked the console in IE? It might be that jQuery is not even being loaded (watch for ‘$ is not defined’ errors). If you need to debug this on mobile, and have no console there, try adding my MobileConsole (http://www.hnldesign.nl/work/code/javascript-mobile-console/)
Thank you for this example.
I am working on a site that uses jQuery hover triggers for displaying submenus and following your example helped me out with this issue on touch devices.
Cheers!
I have some image rollovers that display links that I’d like to use your code on but it doesn’t seem to work when I try to apply it to a hover state on a div (not a link). So for example:
$(‘.myRollover’).on(“touchstart”, function (e) {
etc.
Doesn’t seem to work. Am I missing something? Thanks!
Nevermind, I had a transparent div floating above I forgot about. Took it out and it works great! This is such an awesome little script. I’m using it on all my projects from here on out as I’m a hover junky.
Good to hear you fixed your issue and that the script helps you out. Cheers
thanks a lot, you made my day, this solution will help to solve my problem with hover menu.
Thanks for your post!
I have found an issue with your code (tested on smartphone with your link jsfiddle) : when you alternatively touch each link rapidly, one of it stays on state “hover”, even if you touch another link…
It works very well when you tap each link slowly, but not rapidly…
Thanks for you answer.
to avoid the scroll/wipe issue get rid of preventDefault/return false:
https://gist.github.com/serialpark/ef5172de76bdd52414b4
Nice one, storing the url in a temporary data attribute. This will indeed solve the issue. Only caveat I can think of is that when removing the href attribute, a link will be styled differently by the browser, but nonetheless this is a good approach!
Short and simple solution! Thanks for sharing.
[…] article that appears to solve this problem for mobile use so I’ll have to try this out: http://www.hnldesign.nl/work/code/mouseover-hover-on-touch-devices-using-jquery/ […]
Great article, thanks for writing it up
Intelligent solution, thanks
nice simple solution works nicely
Just wanted to add my thanks, great solution.
Thanks for this! I was able to adapt the code to these flip boxes http://andymcfee.com/2012/08/24/css3-flip-cards/
Had to add my thanks for this, works perfectly.
Thanks for this. Worked a charm.
I thought I was going to have to code a duplicate page with a mobile-friendly alternative to the hover function, and everyone has told me that would be the case, but remarkably your page url stays the same on a mobile device and you have achieved it on the single page.
I need the same second tap function on my site, although not being a coder I can’t work out how to adapt your code. http://tailsofhollywood.com/
I don’t need the fancy effect you use, (which is very effective in achieving what you want it to nevertheless) so all I need is the swap image part.
Any help you can provide would be fantastic – thanks! I would of course attribute.
Thanks for the tip. It will be interesting to see how things change on the web as the “hover” state on objects becomes more obsolete since more and more people are using touch devices.
Thanks for this blog post. While it wasn’t the exact solution I needed, it led me to customizing it so I could solve a similar issue. It might be worth noting this page if you only want this behavior on devices like tablets. http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
Nice one, thanks. Great to hear my post was of use to you!
There is an issue with your code. If i try to scroll (start over an image with a hidden caption) on the touch device, it doesn’t scroll. Over an image with a visible caption it works.
Correct, the script does not provide an option for this. When an image has its caption shown (has been tapped once), you can see the event returns true (line 6), thus continuing ‘normal operation’ (eg. scrolling, or whatever the touch gesture is causing the device to do). Though when the image has not been tapped yet, it performs a ‘preventDefault’ (basically the same as a return false), prohibiting other actions to execute. As this is the ‘core’ of the script (preventing the first tap), I cannot change that for you I’m affraid.