Blog (my web musings)

Find out what's interesting me, get tips, advice, and code gems that don't fit elsewhere.

Search Blog

One line of JavaScript to fix the 'sticky hover' problem on iOS; Where hover CSS isn't removed from an active element until another focusable element (button, link) is clicked. With this script, a tap of the surrounding area will remove hover CSS.


What's the problem?

In the opening paragraph, I state that a problem with hover activated CSS on iOS is that hover styles aren't removed until another element takes focus. A notable case for this is with CSS-only dropdown menus - the kind we see on desktop, which an iPad held in landscape orientation may also be big enough to accommodate.

Picture these steps;

  1. An iPad user activates the desktop CSS menu,
  2. Sub-menus dropdown over content below,
  3. The dropped / revealed menu does not hide again until something else is tapped...

... But how can a user make the decision to tap "something else" with half the screen obscured?

JavaScript-enhanced menus may have already tackled this issue in a few ways;

  • There may be a closing mechanism applied to the parent button - a tap on the same button that reveals the sub-menu, also hides it again.
  • There may be a closing mechanism that is triggered with a tap elsewhere on the screen.
  • There may be a user-intent delay - where the menu will "unhover" when there has been no interaction with it for a certain amount of time.

* But what about our CSS-only hover menu? How can we quickly "unhover" it without introducing JavaScript?

An interesting question. CSS animations may provide a CSS-only way to introduce a user-intent delay. I haven't tested the idea (yet), but thinking about it in the here and now, it seems plausible that the transition-delay: time; property may offer a way to automatically hide a dropdown, but I'm not sure how well it would translate between mouse and touch; Hiding the menu quickly enough when a mouse has moved away from it, AND offering enough of a delay on touch to give the user time to read, process, and choose a menu option. The mouse / desktop scenario is capable of identifying both the user's intent to open and close the menu (albeit with a delayed close) but the touch / mobile scenario only really identifies the user's intent to open the menu - the closing action is automatic, on a timed delay.

Hmmmm, so maybe CSS can't really offer an ideal solution. What about JavaScript? Let's revise the earlier question...

* But what about our CSS-only hover menu? How can we quickly "unhover" it without introducing a lot of JavaScript?

A small and quick JavaScript fix

Here's something that I identified as a happy side-effect while exploring how to beat the 300ms delay on touch -- the CSS-only menu closes with a tap anywhere else on the screen -- but I wasn't thrilled about using a 25kb script, or even a 5kb script, just to hide open menus. So I isolated the pertinent part of the code to this;

(function(l){var i,s={touchend:function(){}};for(i in s)l.addEventListener(i,s)})(document); // sticky hover fix in iOS

The mere presence of this line somewhere in your script file (or on your web page, in <script> tags) will tame those content-obscuring dropdowns and send them cowering for cover with a tap anywhere on the screen. Sweet!

Check out the demo to compare the effect on a menu (before and after) - iOS 'Sticky Hover' Fix - for iPad/ iPhone:


 I hope you find it useful.


Please test thoroughly in your own particular website setup because it may or may not introduce quirky behaviours in other script utilities.