Esto es lo que se me ocurrió hasta ahora después de estudiar el resto de las respuestas. Debería ser capaz de admitir usuarios táctiles, de mouse o híbridos.
Cree una clase de desplazamiento independiente para el efecto de desplazamiento. Por defecto, agregue esta clase de desplazamiento a nuestro botón.
No queremos detectar la presencia de soporte táctil y deshabilitar todos los efectos de desplazamiento desde el principio. Como mencionan otros, los dispositivos híbridos están ganando popularidad; las personas pueden tener soporte táctil pero quieren usar un mouse y viceversa. Por lo tanto, solo elimine la clase de desplazamiento cuando el usuario realmente toque el botón.
El siguiente problema es, ¿qué pasa si el usuario quiere volver a usar el mouse después de tocar el botón? Para resolver eso, necesitamos encontrar un momento oportuno para volver a agregar la clase de desplazamiento que hemos eliminado.
Sin embargo, no podemos volver a agregarlo inmediatamente después de eliminarlo, porque el estado de desplazamiento todavía está activo. Es posible que no queramos destruir y recrear todo el botón también.
Entonces, pensé en usar un algoritmo de espera ocupada (usando setInterval) para verificar el estado de desplazamiento. Una vez que el estado de desplazamiento está desactivado, podemos volver a agregar la clase de desplazamiento y detener la espera ocupada, volviendo al estado original donde el usuario puede usar el mouse o el tacto.
Sé que la espera ocupada no es tan buena, pero no estoy seguro si hay un evento apropiado. He considerado agregarlo nuevamente en el evento mouseleave, pero no fue muy robusto. Por ejemplo, cuando aparece una alerta después de tocar el botón, la posición del mouse cambia pero el evento mouseleave no se activa.
var button = document.getElementById('myButton');
button.ontouchstart = function(e) {
console.log('ontouchstart');
$('.button').removeClass('button-hover');
startIntervalToResetHover();
};
button.onclick = function(e) {
console.log('onclick');
}
var intervalId;
function startIntervalToResetHover() {
// Clear the previous one, if any.
if (intervalId) {
clearInterval(intervalId);
}
intervalId = setInterval(function() {
// Stop if the hover class already exists.
if ($('.button').hasClass('button-hover')) {
clearInterval(intervalId);
intervalId = null;
return;
}
// Checking of hover state from
// http://stackoverflow.com/a/8981521/2669960.
var isHovered = !!$('.button').filter(function() {
return $(this).is(":hover");
}).length;
if (isHovered) {
console.log('Hover state is active');
} else {
console.log('Hover state is inactive');
$('.button').addClass('button-hover');
console.log('Added back the button-hover class');
clearInterval(intervalId);
intervalId = null;
}
}, 1000);
}
.button {
color: green;
border: none;
}
.button-hover:hover {
background: yellow;
border: none;
}
.button:active {
border: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='myButton' class='button button-hover'>Hello</button>
Editar: Otro enfoque que probé es llamar e.preventDefault()
dentro de ontouchstart o ontouchend. Parece detener el efecto de desplazamiento al tocar el botón, pero también detiene la animación de clic del botón y evita que se llame a la función de clic cuando se toca el botón, por lo que debe llamarlos manualmente en el controlador ontouchstart o ontouchend. No es una solución muy limpia.