Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable hover effects on mobile browsers

People also ask

How do I turn off hover effect?

To remove the CSS hover effect from a specific element, you can set the pointer-events property of the element (the hover behavior of which you want to disable) to “none”.

Is there a hover effect on mobile?

Hover effects inform users what they can interact with by providing visual feedback on buttons. But there's a problem — hover effects are for desktop apps, not mobile apps. There are no mouse devices on mobile, so users don't have the luxury of using hover effects.

How do I get rid of hover styles on touch devices?

You can remove all the CSS rules containing :hover using Javascript. This has the advantage of not having to touch CSS and being compatible even with older browsers. Limitations: stylesheets must be hosted on the same domain (that means no CDNs).

How do you prevent sticky hover effects for buttons on touch devices?

preventDefault() within ontouchstart or ontouchend. It appears to stop the hover effect when the button is touched, but it also stops the button click animation and prevents the onclick function from being called when the button is touched, so you have to call those manually in the ontouchstart or ontouchend handler.


I take it from your question that your hover effect changes the content of your page. In that case, my advice is to:

  • Add hover effects on touchstart and mouseenter.
  • Remove hover effects on mouseleave, touchmove and click.

Alternatively, you can edit your page that there is no content change.

Background

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):

  1. touchstart
  2. touchmove
  3. touchend
  4. 300ms delay, where the browser makes sure this is a single tap, not a double tap
  5. mouseover
  6. mouseenter
    • Note: If a mouseover, mouseenter or mousemove event changes the page content, the following events are never fired.
  7. mousemove
  8. mousedown
  9. mouseup
  10. 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.

Solution Explained

To recap:

  • Add hover effects on touchstart and mouseenter.
  • Remove hover effects on 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.

Alternative Solutions

I've found the following problems with the following alternatives:

  • browser detection: Extremely prone to errors. Assumes that a device has either mouse or touch, while a combination of both will become more and more common when touch displays prolifirate.
  • CSS media detection: The only CSS-only solution I'm aware of. Still prone to errors, and still assumes that a device has either mouse or touch, while both are possible.
  • Emulate the click event in 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.
  • Use a variable to suppress mouse events: This set a variable in 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. See Walter Roman's answer on this page. 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).

Further Reading

  • http://jsfiddle.net/macfreek/24Z5M/. Test the above solution for yourself in this sandbox.
  • http://www.macfreek.nl/memory/Touch_and_mouse_with_hover_effects_in_a_web_browser. This same answer, with a bit more background.
  • https://www.html5rocks.com/en/mobile/touchandmouse/. Great background article on html5rocks.com about touch and mouse in general.
  • https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html. Safari Web Content Guide - Handling Events. See in particular figure 6.4, which explains that no further events are fired after a content change during a mouseover or mousemove event.

How can I get the hover effects when I'm using the mouse, but suppress them when I'm using the touchscreen?

Maybe don't think of it so much as suppressing hover effects for touchscreens, but as adding hover effects for mouse events?

If you want to keep the :hover effects in your CSS you could specify different styles for different media:

@media screen { /* hover styles here */ } 

@media handheld { /* non-hover styles here */ }

Except that unfortunately there are plenty of mobile devices that ignore this and just use the screen rules. Fortunately a lot of newer mobile/tablet browsers do support some fancier media queries:

@media screen and (max-width:800px) { /* non-hover styles here */ }

So even if the "screen" or "handheld" part is ignored the "max-width" will do the trick for you. You could just assume that anything with a screen smaller than 800 pixels must be a tablet or phone, and not use hover effects. For the rare users who are using a mouse on a low resolution device they wouldn't see the hover effects but your site would be fine otherwise.

Further reading on media queries? There are plenty of articles about this online - here is one: http://www.alistapart.com/articles/return-of-the-mobile-stylesheet

If you shift the hover effects out of your CSS and apply them with JavaScript then you could bind specifically to mouse events, and/or again you could just make some assumptions just based on screen size with the worst-case "problem" being that some user who is using a mouse misses out on the hover effects.


I wrote the following JS for a recent project, which was a desktop/mobile/tablet site that has hover effects that shouldn't appear on-touch.

The mobileNoHoverState module below has a variable preventMouseover (initially declared as false), that is set to true when a user fires the touchstart event on an element, $target.

preventMouseover is then being set back to false whenever the mouseover event is fired, which allows the site to work as intended if a user is using both their touchscreen and mouse.

We know that mouseover is being triggered after touchstart because of the order that they are being declared within init.

var mobileNoHoverState = function() {

    var hoverClass = 'hover',
        $target = $(".foo"), 
        preventMouseover = false;

    function forTouchstart() {
        preventMouseover = true;
    }

    function forMouseover() {
        if (preventMouseover === false) {
            $(this).addClass(hoverClass);
        } else {
            preventMouseover = false;
        }
    }

    function forMouseout() {
        $(this).removeClass(hoverClass);
    }

    function init() {
        $target.on({
            touchstart  : forTouchstart,
            mouseover   : forMouseover,
            mouseout    : forMouseout
        });                
    }

    return {
        init: init
    };
}();

The module is then instantiated further down the line:

mobileNoHoverState.init();

I really wanted a pure css solution to this myself, since sprinkling a weighty javascript solution around all of my views seemed like an unpleasant option. Finally found the @media.hover query, which can detect "whether the primary input mechanism allows the user to hover over elements." This avoids touch devices where "hovering" is more of an emulated action than a direct capability of the input device.

So for example, if I have a link:

<a href="/" class="link">Home</a>

Then I can safely style it to only :hover when the device easily supports it with this css:

@media (hover: hover) {
  .link:hover { /* hover styles */ }
}

While most modern browsers support interaction media feature queries, some popular browsers such as IE and Firefox do not. In my case this works fine, since I only intended to support Chrome on desktop and Chrome and Safari on mobile.


My solution is to add hover-active css class to the HTML tag, and use it on the beginning of all the CSS selectors with :hover and remove that class on the first touchstart event.

http://codepen.io/Bnaya/pen/EoJlb

JS:

(function () {
    'use strict';

    if (!('addEventListener' in window)) {
        return;
    }

    var htmlElement = document.querySelector('html');

    function touchStart () {
        document.querySelector('html').classList.remove('hover-active');

        htmlElement.removeEventListener('touchstart', touchStart);
    }

    htmlElement.addEventListener('touchstart', touchStart);
}());

HTML:

<html class="hover-active">

CSS:

.hover-active .mybutton:hover {
    box-shadow: 1px 1px 1px #000;
}