iOS Mobile Safari Hover Menu Fix

By  on  

One component I'm quite proud of creating is the Mozilla Developer Network's dropdown menu component.  It's elegant, flexible, accessible via keyboard, and relatively compact (though it does require jQuery).  One problem I did notice, however, was that tapping once opened the menu (good) but you'd have to double tap (tap twice) on a submenu link to trigger a move to the link URL.  Not a catastrophic issue but certainly annoying. After a bit of tinkering I came up with a solution which satisfied the annoyance!

The JavaScript

I found out the core issue:  the main menu item was coded to open the submenu upon mouseenter.  While I was grateful that iOS mobile Safari was using mouseenter as simply an open trigger, it was causing users the double tap pain to visit a link in the submenu.  So I took advantage of the touchstart event:

$menuItem.on('touchstart mouseenter focus', function(e) {
    if(e.type == 'touchstart') {
        // Don't trigger mouseenter even if they hold
        e.stopImmediatePropagation();
        // If $item is a link (<a>), don't go to said link on mobile, show menu instead
        e.preventDefault();
    }

    // Show the submenu here
});

Why not touchend?  Unfortunately if the user held their finger down for more than a quick tap, the mouseenter event would trigger.  A bit odd but since I have touchstart as the first event in the listener, I can use stopImmediatePropagation to prevent the mouseenter from ever firing.  What's also nice is I don't have to do any device detection to juggle touchstart and mouseenter, so this is an awesome solution...

...but not a perfect solution.  If you tap and hold the main menu item link, the mobile browser context menu doesn't appear because we've used preventDefault.  I'm still exploring and experimenting so I'll continue to update this post as I search for the holy grail!

Recent Features

  • By
    Responsive and Infinitely Scalable JS Animations

    Back in late 2012 it was not easy to find open source projects using requestAnimationFrame() - this is the hook that allows Javascript code to synchronize with a web browser's native paint loop. Animations using this method can run at 60 fps and deliver fantastic...

  • By
    5 More HTML5 APIs You Didn&#8217;t Know Existed

    The HTML5 revolution has provided us some awesome JavaScript and HTML APIs.  Some are APIs we knew we've needed for years, others are cutting edge mobile and desktop helpers.  Regardless of API strength or purpose, anything to help us better do our job is a...

Incredible Demos

  • By
    MooTools&#8217; AutoCompleter Plugin

    One of the famous MooTools plugins is Harald Kirschner's AutoCompleter plugin. AutoCompleter takes a term input by the user and searches for matches -- an obviously help to the user. Here's how to make the most of Harald's great plugin. The XHTML All we...

  • By
    Xbox Live Gamer API

    My sharpshooter status aside, I've always been surprised upset that Microsoft has never provided an API for the vast amount of information about users, the games they play, and statistics within the games. Namely, I'd like to publicly shame every n00b I've baptized with my...

Discussion

  1. James

    Best solution I’ve seen so far. Tried using touchend, and it was firing twice or more on Android devices. Did play around with a timeout, that worked, but felt wrong. This solution works on everything I’ve tested so far, bar the minor issue of holding down the touch you mention.

  2. Is there anyway to do that without jQuery? I’ve been trying but it doesn’t seem to work:

    menuItem.addEventListener( 'touchstart mouseenter focus', toggleMenu );
    

    And the toggleMenu would be like this:

    function toggleMenu() {
        if(e.type == 'touchstart') {
            // Don't trigger mouseenter even if they hold
            e.stopImmediatePropagation();
            // If menuItem is a link (), don't go to said link on mobile, show menu instead
            e.preventDefault();
        }
        // toggle code here
    }
    

    Sorry if this sound silly, but I’m very much new to javascript.

    Anyway, you’re blog is helping me a lot, thanks!

  3. dan

    any idea how I could trigger the click in this case?

    I’m wanting to show the menu (in my case a tooltip) on touchstart, but I actually follow the link on touchend

  4. THANK YOU SO MUCH FOR THIS changing my listener from mouseenter to touchstart immediately solved the problem for me.

    I literally spent an hour fiddling with the CSS because I thought it was something to do with pseudo elements. :/

  5. Dave

    Thank you. I have solved the Upload link need to double tap in iOS issue with your help.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!