Hover effects with mouse and touch.

Demonstration how to support hover effects on both mouse and touch devices, by only binding to the proper events. No browser- or media detection (so it works on devices with both mouse and touch!), no preventDefault() or clumsy simulation of click events is necessary.

by Freek Dijkstra

HTML

<p>Demonstration of hover effects with mouse and touch. No browser- or media detection (so it works on devices with both mouse and touch!), no preventDefault() or clumsy simulation of click events is necessary.</p>

<p>See <a href="http://www.macfreek.nl/memory/Touch_and_mouse_with_hover_effects_in_a_web_browser">http://www.macfreek.nl/memory/Touch_and_mouse_with_hover_effects_in_a_web_browser</a>.</p>

<p>Just:</p>
<ul>
    <li>Add hover effects on <code>touchstart</code> and <code>mouseenter</code>.</li>
    <li>Remove hover effects on <code>mouseleave</code>, <code>touchmove</code> and <code>click</code>.</li>
    <li>Ignore all those <code>touchend</code> events!</li>
</ul>

<div class="tests">
	<a href="http://stackoverflow.com/" id="pure_css" class="test">1. regular :hover effect with CSS<div class="content">more info</div></a>
	<a href="http://stackoverflow.com/" id="regular_js" class="test">2. JavaScript-based hover<div class="content">more info</div></a>
	<a href="http://stackoverflow.com/" id="show_on_touch" class="test">3. JavaScript-based hover as #2 with highlight on touchstart<div class="content">more info</div></a>
	<a href="http://stackoverflow.com/" id="support_cancel" class="test">4. JavaScript-based hover as #3 with reset on touchmove<div class="content">more info</div></a>
</div>

<p>Explanation:</p>
<ol>
    <li>This is a regular hover effect using CSS. Note that it is possible to show and hide content by hovering using pure CSS. On a touchpad, you will have to click this link twice in order to follow it. This is because a content change on a mouseover will prevent the click event from firing.</li>
    <li>This is the very some hover effect, only implemented in JavaScript.  On a touchpad, you still have to click this link twice in order to follow it.</li>
    <li>This time, the hover effect is already started on touchstart. First, this make the hover faster on touch. Secondly, the mouseover event does not change the state (the state has...

CSS

.test { 
    position: relative; 
    display: block; 
    height: 40px; 
    margin: 1em; 
    padding-top: 20px; 
    border: 1px solid black; 
    text-align:center 
}
.content { 
    position: absolute; 
    right: 10px; top: 10px; 
    display: none; 
}
#pure_css:hover { background: yellow; }
#pure_css:hover .content { display: block; }
#regular_js.hover { background: yellow; }
#show_on_touch.hover { background: yellow; }
#support_cancel.hover { background: yellow; }

JavaScript

$('#regular_js').mouseenter( function(e) {
	$('#regular_js').addClass('hover');
	$('#regular_js .content').show();
}).mouseleave( function(e) {
	$('#regular_js').removeClass('hover');
	$('#regular_js .content').hide();
}).click( function(e) {
	$('#regular_js').removeClass('hover');
	$('#regular_js .content').hide();
});

$('#show_on_touch').on('touchstart', function(e) {
	$('#show_on_touch').addClass('hover');
	$('#show_on_touch .content').show();
}).mouseenter( function(e) {
	$('#show_on_touch').addClass('hover');
	$('#show_on_touch .content').show();
}).mouseleave( function(e) {
	$('#show_on_touch').removeClass('hover');
	$('#show_on_touch .content').hide();
}).click( function(e) {
	$('#show_on_touch').removeClass('hover');
	$('#show_on_touch .content').hide();
});

$('#support_cancel').on('touchstart', function(e) {
	$('#support_cancel').addClass('hover');
	$('#support_cancel .content').show();
}).on('touchmove', function(e) {
	console.log('mousemove: remove highlight');
	$('#support_cancel').removeClass('hover');
	$('#support_cancel .content').hide();
}).mouseenter( function(e) {
	$('#support_cancel').addClass('hover');
	$('#support_cancel .content').show();
}).mouseleave( function(e) {
	$('#support_cancel').removeClass('hover');
	$('#support_cancel .content').hide();
}).click( function(e) {
	$('#support_cancel').removeClass('hover');
	$('#support_cancel .content').hide();
});

$('div').on('click', function(e) {
	console.log("** cancel click event (stay on page for debug purposes) **");
	e.preventDefault();
});