I\'m having a very similar problem to iPad/iPhone hover problem causes the user to double click a link where a user tapping a link has to tap twice to actually go to it.
<My advice is to:
touchstart
and mouseenter
.mouseleave
, touchmove
and click
.This is similar to Jake's answer, but removes the need to emulate the click event.
In order to simulate a mouse, browsers such as Webkit mobile fire the following events if a user touches and releases a finger on touch screen (like iPad) (source: Touch And Mouse on html5rocks.com):
touchstart
touchmove
touchend
mouseover
mouseenter
mouseover
, mouseenter
or mousemove
event changes the page content, the following events are never fired.mousemove
mousedown
mouseup
click
It does not seem possible to simply tell the webbrowser to skip the mouse events.
What's worse, if a mouseover event changes the page content, the click event is never fired, as explained on Safari Web Content Guide - Handling Events, in particular figure 6.4 in One-Finger Events. What exactly a "content change" is, will depend on browser and version. I've found that for iOS 7.0, a change in background color is not (or no longer?) a content change.
To recap:
touchstart
and mouseenter
.mouseleave
, touchmove
and click
.Note that there is no action on touchend
!
This clearly works for mouse events: mouseenter
and mouseleave
(slightly improved versions of mouseover
and mouseout
) are fired, and add and remove the hover.
If the user actually click
s a link, the hover effect is also removed. This ensure that it is removed if the user presses the back button in the web browser.
This also works for touch events: on touchstart
the hover effect is added. It is '''not''' removed on touchend
. It is added again on mouseenter
, and since this causes no content changes (it was already added), the click
event is also fired, and the link is followed without the need for the user to click again!
The 300ms delay that a browser has between a touchstart
event and click
is actually put in good use because the hover effect will be shown during this short time.
If the user decides to cancel the click, a move of the finger will do so just as normal. Normally, this is a problem since no mouseleave
event is fired, and the hover effect remains in place. Thankfully, this can easily be fixed by removing the hover effect on touchmove
.
That's it!
Note that it is possible to remove the 300ms delay, for example using the FastClick library, but this is out of scope for this question.
I've found the following problems with the following alternatives:
touchend
: This will incorrectly follow the link, even if the user only wanted to scroll or zoom, without the intention of actually clicking the link.touchend
that is used as a if-condition in subsequent mouse events to prevents state changes at that point in time. The variable is reset in the click event. This is a decent solution if you really don't want a hover effect on touch interfaces. Unfortunately, this does not work if a touchend
is fired for another reason and no click event is fired (e.g. the user scrolled or zoomed), and is subsequently trying to following the link with a mouse (i.e on a device with both mouse and touch interface).mouseover
or mousemove
event.See also Disable hover effects on mobile browsers.
Here's what I ended up doing:
The problem is that touchstart and touchend only know about touch events, not scroll events, so they only react to starting the touch and ending the touch. What we have to do is distinguish between scrolling and not scrolling. Here's what I did:
$('a')
.live('touchstart', function(){
isScrolling = false;
})
.live('touchmove', function(e){
isScrolling = true;
})
.live('touchend', function(e){
if( !isScrolling )
{
window.location = $(this).attr('href');
}
});
This does these things in order:
Edit: A while after this, I discovered the problem was being caused by SuperFish. Disabling superfish when the page was under a certain width solved the problem.
If your content is in a UIScrollView
, you can (or may already have) implemented the UIScrollViewDelegate
; which contains the following method:
-(void)scrollViewDidScroll: (UIScrollView*)scrollView
If you use this, and get the following:
float offset = scrollView.contentOffset.y;
if (offset > 0)
// then we have started to scroll
If you use this condition in conjunction with detecting the number of taps/presses currently on the screen, (ie, just one in your case) then ignore any fired calls (if >1 taps) that may occur when a link is tapped.
Hope this helps!