A click in iOS Safari triggers a "hover state" on element underneath where you tapped

18,809

Solution 1

If you have option to disable hover then you can do that with bit of hack.

First add this line in "DOMContentLoaded" event handler.

var canHover = !(matchMedia('(hover: none)').matches);
if (canHover) {
  document.body.classList.add('can-hover');
}

It will add .can-hover class to body of your html document.

Then just edit a:hover rule to match this instead

your css rule:

a:hover {
  color: red;
}

should be:

.can-hover a:hover {
  color: red;
}

this should prevent devices with touch screen to apply hover style to any anchor.

Solution 2

Have you tried using a media query to test for :hover capability?

See https://drafts.csswg.org/mediaqueries/#hover

Solution 3

...it can be clearly seen that this is the intended behavior on iOS even when swiping on a list of hover-able items.

If what you are trying to avoid is just the hover effect, here's what I would do. I would get the device using a JS function, apply a class on the body tag and remove any hover effects as needed.

Javascript:

function isAppleDevice() {
  return (
    (navigator.userAgent.toLowerCase().indexOf("ipad") > -1) ||
    (navigator.userAgent.toLowerCase().indexOf("iphone") > -1) ||
    (navigator.userAgent.toLowerCase().indexOf("ipod") > -1)
   );
}
if (isAppleDevice() {
  $('body').addClass('is_apple')
}

CSS:

.is_apple a:hover {
  // same styling as not hovering
}

Solution 4

This seems to be intended behaviour in Safari. Apple's documentation says that single-finger taps are handled as mouse events, and shows which events are raised during a tap. There's a mouseover at the start of the tap, but a mouseout is only triggered when you click on something else. This implies that Safari assumes that the pointer stays in the same place until the next tap, which would mean it makes sense for the lower element to be in the hover state.

I tried your code in Chrome on Android, and Chrome and Firefox on Windows (I don't have an Apple device). In all three cases, when I click on the <div> using a mouse (over the link), the hover style is applied to the link afterwards, unless I move the cursor away from the link before I release the mouse. If I tap on the <div> with my finger on the touchscreen, the link does not get the hover style.

Firefox and Chrome seem to be consistent with each other for touch and mouse, but Safari seems (by design) to treat the touchscreen as a mouse: it behaves the same way for a touch press as the Chrome & Firefox do for a mouse click.

I don't this that this is a bug you need to fix or work around. Unless you know all your users will be using a specific hardware/browser combination, you probably need to plan for the eventuality that they could use a mouse, a touchscreen, or perhaps both at the same time; and design a UI that works for all these cases. My Windows laptop and Android tablet both have mousepads as well as touchscreens, and I sometimes use a USB OTG mouse with my Android phone.

You could also look at the Pointer Events API, which works in Chrome, IE and Edge (not to be confused with the pointer-events CSS property mentioned by @erier). As the API deals with events rather than CSS, this won't directly help get your styling right, but shows the efforts going on to make browsers react consistently to different types of input device.

Solution 5

Try the new media query method to test whether the user's primary input mechanism can hover over elements.

@media (hover) {
  // Your hover effects
  a:hover {
    color: red;
  }
}

Docs: hover - CSS: Cascading Style Sheets | MDN

Share:
18,809

Related videos on Youtube

romellem
Author by

romellem

Mo' math, mo' problems.

Updated on June 25, 2022

Comments

  • romellem
    romellem almost 2 years

    On iOS Safari 11, if I have a <div> positioned over an element that has a :hover effect, and the <div> has an event that makes it disappear when clicked, then my link "underneath" gets the hover effect applied after the element is removed from the DOM.

    See below for an animated GIF of what I'm talking about:

    iOS click through

    I've given the button a see-through background so you can see the link behind it. When I tap the button in on a spot where the link is not located, the link (i.e. Some link) stays blue and does not change to its hover state of red.

    However, when I tap the div in a spot that happens to be directly over the link, after the div is removed from the DOM, the link gets its hover state applied.

    Clicking the link after each of these correctly triggers its on.click event (an alert window).

    I do not see this issue on android on Chrome (see example below):

    Android click through working

    Below you'll also find the sample HTML/CSS/JS I used; the setup is pretty simple.

    I'd like to have iOS act in the same way Android Chrome does: that is, clicking on an element that is immediately removed from the DOM should not trigger a :hover state for an element immediately behind it.

    var button = document.querySelector(".button");
    button.addEventListener("click", function() {
      button.parentNode.removeChild(button);
    });
    
    var link = document.querySelector('a');
    link.addEventListener("click", function() {
      window.alert('clicked!');
    });
    a:link    { color: blue }
    a:visited { color: blue }
    a:hover   { color: red }
    a:active  { color: green }
    
    .button {
        background: rgba(100, 0, 0, .4);
        position: absolute;
      
        top: 2px;
        left: 2px;
        z-index: 1;
    
        width: 100px;
        height: 100px;
        line-height: 100px;
    
        text-align: center;
        border-radius: 5px;
    }
    
    .button:hover {
        background: rgba(0, 100, 0, .4);
        cursor: pointer;
    }
    <a href="#">Some link</a>
    <div class="button">Click me!</div>
  • Joe Hansen
    Joe Hansen over 6 years
    I like your answer because I think the goal is to have hover effect when there is a mouse, and checking for "ipad", "iphone", or "ipod" covers that decision.
  • scooterlord
    scooterlord over 6 years
    @JosephHansen I love your spirit, that even though you have your own answer, you recognize one other's reply. I wish everyone in here was like you! Kudos!
  • romellem
    romellem over 6 years
    Still not the prettiest, but I like this better since it uses Feature Detection rather than User-Agent sniffing. Good enough for me.
  • Milan Jaric
    Milan Jaric over 6 years
    Maybe you could use modernizr to avoid writing code as I used in answer, so your app code is more cleaner, without hacks. Anyways, I'm not sure if you can fix your example without javascript involved since it is cross browser inconsistency issue in css and html engine. I'm a bit opinionated that there is no true reason to put a lot of effort in making page look same in all browsers unless difference is show stopper. After all, most consumers are using single browser, they are not wasting their time to compere how your page looks in each browser.