Thursday, July 18, 2013

Removing a top mega-menu link on mobile browsers

Here's the task I needed to solve (requested by the customer, but sort of makes sense). A site has a mega-menu, displayed on hover. Looks good etc, except for when you try to view it on a touchscreen device, mobile, tablet or what have you. Since it lacks hover (along with many other useful things — but I digress), you can't actually invoke the mega-menu, and all the designer's efforts go to waste. What a shame!

A mockup of the setup.

The suggested solution for hovers is to add :focus selectors to all :hover selectors — meaning the necessary functionality will work when you also touch the corresponding element. Yet there is one small nuance — if the element in question is a link, then by touching it you are teleported to the link's destination. You may see a flash of that mega-menu hovering before you depart, as an additional smirk of fate.

One possible way to solve it, born in discussions with the client, is to disable the link on the mobile devices. So, on desktops you can hover — and see the mega-menu, and you can also click — to move on to your destination. On mobiles/tablets you can touch to open the mega-menu, and touch again to close it. That's the idea. Note that this solution is only viable if the main link is duplicated within the mega-menu itself — in our case it is.

The solution is two-fold. First, we need to remove the link from mobile browsers.

Remove Link from Mobile

Let's assume the following structure for your main menu:
<ul id="main-menu">
   <li id="menu-item-1"><a href="/the-link">Mega-menu link</a>
      <ul class="sub-menu">
         <li><a href="#">Mega-menu Title</a></li>
         <li><a href="#">Item 1</a></li>
         <li><a href="#">...</a></li>
      </ul>
   </li>
   <li id="menu-item-2"><a href="/also-link">Another link</a></li>
   <li>...</li>
</ul>

So what we need to do is a) detect if we are on a mobile platform, and b) remove the link.

Detection code (found in a helpful StackOverflow answer, there were actually many options, yet this one is of the more readable kind, so will be easy to make additions to if/when new platforms and situations occur):
<script type="text/javascript">
   jQuery(document).ready(function() {
      var isMobile = {
          Android: function() {
             return navigator.userAgent.match(/Android/i);
          },
          BlackBerry: function() {
             return navigator.userAgent.match(/BlackBerry/i);
          },
          iOS: function() {
             return navigator.userAgent.match(/iPhone|iPad|iPod/i);
          },
          Opera: function() {
             return navigator.userAgent.match(/Opera Mini/i);
          },
          Windows: function() {
             return navigator.userAgent.match(/IEMobile/i);
          },
          any: function() {
             return (isMobile.Android() || isMobile.BlackBerry() 
                || isMobile.iOS() || isMobile.Opera() 
                || isMobile.Windows());
          }
      };

      if(isMobile.any()) {
         // Mobile browser
         // ...do your thing here
      } else {
         // Desktop browser, just in case
      }
   });
</script>

You see that I used jQuery's “ready” event, yet you can have this code on the document load event, if you don't use jQuery (I do use it for actual removing of the link below, so — beware).

Then, we need to get rid of the <a> tag. One of the ways is to drill into its contents and just drop the parent tag — using $('#your-id a').contents().unwrap(), yet in my case I need a block element within the <li> for styling (don't ask), so my code is a tad more complicated:
jQuery('#menu-item-1 > a')
   .replaceWith('<span class="no-link">Open Mega Menu</span>');

This places a <span> tag instead of the <a> tag. Then I made sure that styling for menu links is also applied to .no-link class — and all is good.

Enable Hover Action on Mobile

Next step — dealing with the mega-menu itself. As testing shows, duplicating the :hover styles for :focus also works to show the menu, as follows:
.main-menu li:hover > ul.sub-menu,
.main-menu li:focus > ul.sub-menu {
 display: block;
}

This has one caveat — the mega-menu doesn't then close, yet for my purposes it is acceptable to stop here and not overcomplicate things. If you have experience on how to reliably solve it — please let me know.

Actually, coding for mobile feels like the wild 90's again, every browser seems set on creating their own standard of dealing with things. Oh well, I guess give it a decade and it'll settle.

Anyway, I hope you find this useful, let me know if you have any comments / corrections / additions. Thanks and take care!

No comments:

Post a Comment