Sam Willis
Sam Willis

Reputation: 4211

Menu open by hover on desktop, click on mobile

I am trying to create a menu button that when hovered, reveals a drop down set of links on desktop. However, obviously users can't hover on mobiles etc. so have been trying to work out how to make the button reveal the menu on click too.

I have so far managed to come up with adding and removing the .show-nav class on mouse enter and leave using jquery. I tried adding the following code block but it obviously also affects desktop, which makes the menu kinda screwy if someone clicks the button (as the class is applied on the hover and then added and removed permanently using the click).

Thanks to cesare everything is working as it should, apart from in Chrome on iOS. djtwigg's solution is working in Chrome but not safari. Is it possible to merge the two solutions together?

<nav>
    <ul>
        <li id="nav-button">
            <a id="nav-click" href="#">Menu <i class="fa fa-bars"></i></a>
            <ul class="sub-nav">
                <li><a href="#">Link1</a></li>
                <li><a href="#">Link2</a></li>
                <li><a href="#">Link3</a></li>
                <li><a href="#">Link4</a></li>
            </ul>
        </li>
    </ul>
</nav>

var flag = false;
container.bind('touchstart', function(){
if (!flag) {
   flag = true;
   setTimeout(function(){ flag = false; }, 260);
   list.toggleClass('show-nav');
}
return false
});

container.hover(function(){
   list.addClass('show-nav');
}, function(){
   list.removeClass('show-nav');    
});
});

Upvotes: 1

Views: 5573

Answers (3)

Daniel Twigg
Daniel Twigg

Reputation: 759

It is possible to do this by checking whether the event was a touch or click. I found a plugin that helps: Gist of plugin

Simply check for a click and if it was a touch-click then toggle the menu. Use preventDefault() as shown in the fiddle. Add or remove the class on mouseenter and mouseleave by checking if it was a mouse instead of touch.

    // Bind mouse events to links.
  container
    .click(myClickCallback)
    .mouseenter(myMouseenterCallback)
    .mouseleave(myMouseleaveCallback);

  // Click callback.
  function myClickCallback(e) {
    var touchOrMouse = $body.touchOrMouse('get', e);

    // If mouse event has been invoked by touch.
    if (touchOrMouse === 'touch') {
      // Toggle .hovered class.
      list.toggleClass('show-nav');
    }

    // Do not follow the link.
    e.preventDefault();
  }

  // Mouse enter callback.
  function myMouseenterCallback(e) {
    var touchOrMouse = $body.touchOrMouse('get', e);

    // If mouse event has not been invoked by touch.
    if (touchOrMouse === 'mouse') {
      // Add .hovered class.
      list.addClass('show-nav');
    }
  }

  // Mouse leave callback.
  function myMouseleaveCallback(e) {
    var touchOrMouse = $body.touchOrMouse('get', e);

    // If mouse event has not been invoked by touch.
    if (touchOrMouse === 'mouse') {
      // Remove .hovered class.
      list.removeClass('show-nav');
    }
  }

Here is a fiddle with a working example on an iPhone: Fiddle

Upvotes: 0

cesare
cesare

Reputation: 2118

You can use "hover" for desktop and bind "touchstart" for mobile.

var flag = false;
container.bind('touchstart', function(){
if (!flag) {
   flag = true;
   setTimeout(function(){ flag = false; }, 260);
   list.toggleClass('show-nav');
}
return false
});

container.hover(function(){
   list.addClass('show-nav');
}, function(){
   list.removeClass('show-nav');    
});

try this fiddle: https://jsfiddle.net/62cvsvvc/6/

This solution is inspired from this question: How to bind 'touchstart' and 'click' events but not respond to both?

EDIT I checked on my server and works properly. I guess that this issue with iOS is related with jsFiddle enviroment.

Please try on: http://cesare.heliohost.org/test/

Upvotes: 1

taxicala
taxicala

Reputation: 21769

Use the touchstart event:

$('#nav-button').on('touchstart', function(){
     $('.sub-nav').toggleClass('show-nav');
});

Upvotes: 1

Related Questions