Safari provides an easy way to look at the URL under a link. On the Mac, choose View > Show Status Bar, hover your pointer over the link, and look at the bottom of the window. In iOS, touch and hold a link (don't press for 3D Touch) until a popover appears, showing the link and giving you options for opening it.
A "click" on an iPad will often simulate hover behaviors. Please note that the row-click with the side panel showing is not a hover effect but rather a click effect anyway. Cheers!
To answer your main question: “How do I simulate a hover with a touch in touch enabled browsers?” Simply allow 'clicking' the element (by tapping the screen), and then trigger the hover event using JavaScript. var p = document.
Haven't tested this fully but since iOS fires touch events, this could work, assuming you are in a jQuery setting.
$('a').on('click touchend', function(e) {
var el = $(this);
var link = el.attr('href');
window.location = link;
});
The idea is that Mobile WebKit fires a touchend event at the end of a tap so we listen for that and then redirect the browser as soon as a touchend event has been fired on a link.
It is not entirely clear what your question is, but if you just want to eliminate the double click, while retaining the hover effect for the mouse, my advice is to:
touchstart and mouseenter.mouseleave, touchmove and click.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):
touchstarttouchmovetouchendmouseovermouseenter
mouseover, mouseenter or mousemove event changes the page content, the following events are never fired.mousemovemousedownmouseupclickIt 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 clicks 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 iPad/iPhone double click problem and Disable hover effects on mobile browsers.
Seems there is a CSS solution after all. The reason Safari waits for a second touch is because of the background image (or elements) you usually assign on the :hover event. If there is none to be shown - you won't have any problems. The solution is to target iOS platform with secondary CSS file (or style in case of a JS approach) which overrides :hover background to inherit for example and keep hidden the elements you were going to display on mouse over:
Here is an example CSS and HTML - a product block with a starred label on mouse over:
HTML:
<a href="#" class="s"><span class="s-star"></span>Some text here</a>
CSS:
.s {
background: url(some-image.png) no-repeat 0 0;
}
.s:hover {
background: url(some-image-r.png) no-repeat 0 0;
}
.s-star {
background: url(star.png) no-repeat 0 0;
height: 56px;
position: absolute;
width: 72px;
display:none;
}
.s:hover .s-star {
display:block;
}
Solution (secondary CSS):
/* CSS */
/* Keep hovers the same or hidden */
.s:hover {
background:inherit;
}
.s:hover .s-star {
display:none;
}
No need to make overcomplicated.
$('a').on('touchend', function() {
$(this).click();
});
What worked for me is what others here have already said:
Don't show/hide elements on hover or mousemove (which is the event in my case).
Here's what Apple says (https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html):
A clickable element is a link, form element, image map area, or any other element with mousemove, mousedown, mouseup, or onclick handlers
If the user taps a clickable element, events arrive in this order: mouseover, mousemove, mousedown, mouseup, and click. Also, if the contents of the page changes on the mousemove event, no subsequent events in the sequence are sent. This behavior allows the user to tap in the new content.
So, you could use @woop's solution: detect the userAgent, check if it's and iOS device and then bind the event. I ended up using this technique because it suits my needs and it makes more sense do not bind hover events when you don't want it.
But... if you don't wanna mess with userAgents and still hide/show elements on hover/mousemove, i found out you can do so by using native javascript, like this:
$("body").on("mouseover", function() {
document.getElementsByTagName("my-class")[0].style.display = 'block'; //show element
document.querySelector(".my-selector div").style.display = 'none'; // hide element
});
This will work on the Desktop version and will do nothing on the mobile version.
And for a little more compatibility...
$("body").on("mouseover", function() {
if (document.getElementsByTagName && document.querySelector) { // check compatibility
document.getElementsByTagName("my-class")[0].style.display = 'block'; //show element
document.querySelector(".my-selector div").style.display = 'none'; // hide element
} else {
$(".my-class").show();
$(".my-selector div").hide();
}
});
cduruk's solution was quite effective, but caused problems on a few parts of my site. Because I was already using jQuery to add the CSS hover class, the easiest solution was to simply not add the CSS hover class on mobile devices (or more precisely, to ONLY add it when NOT on a mobile device).
Here was the general idea:
var device = navigator.userAgent.toLowerCase();
var ios = device.match(/(iphone|ipod|ipad)/);
if (!(ios)) {
$(".portfolio-style").hover(
function(){
$(this).stop().animate({opacity: 1}, 100);
$(this).addClass("portfolio-red-text");
},
function(){
$(this).stop().animate({opacity: 0.85}, 100);
$(this).removeClass("portfolio-red-text");
}
);
}
*code reduced for illustrative purposes
I had the following problems with the existing solutions, and found something that seems to solve all of them. This assumes you're aiming for something cross browser, cross device, and don't want device sniffing.
Using just touchstart or touchend:
Triggering mouseover events on touchstart and mouseout on touchmove has less serious consequences, but does interfere with the usual browser behaviour, for example:
touchstart like a mouseover, which is mouseouted on the next touchstart. One way to see mouseover content in Android is therefore to touch the area of interest and wiggle your finger, scrolling the page slightly. Treating touchmove as mouseout breaks this.In theory, you could just add a flag with touchmove, but iPhones trigger touchmove even if there's no movement. In theory, you could just compare the touchstart and touchend event pageX and pageY but on iPhones, there's no touchend pageX or pageY.
So unfortunately to cover all bases it does end up a little more complicated.
$el.on('touchstart', function(e){
$el.data('tstartE', e);
if(event.originalEvent.targetTouches){
// store values, not reference, since touch obj will change
var touch = e.originalEvent.targetTouches[0];
$el.data('tstartT',{ clientX: touch.clientX, clientY: touch.clientY } );
}
});
$el.on('touchmove', function(e){
if(event.originalEvent.targetTouches){
$el.data('tstartM', event.originalEvent.targetTouches[0]);
}
});
$el.on('click touchend', function(e){
var oldE = $el.data('tstartE');
if( oldE && oldE.timeStamp + 1000 < e.timeStamp ) {
$el.data('tstartE',false);
return;
}
if( $el.data('iosTouchM') && $el.data('tstartT') ){
var start = $el.data('tstartT'), end = $el.data('tstartM');
if( start.clientX != end.clientX || start.clientY != end.clientY ){
$el.data('tstartT', false);
$el.data('tstartM', false);
$el.data('tstartE',false);
return;
}
}
$el.data('tstartE',false);
In theory, there are ways to get the exact time used for a longpress instead of just using 1000 as an approximation, but in practice it's not that simple and it's best to use a reasonable proxy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With