Search code examples
jqueryhtmlmenuhovertouch

Hover menu is not working on touch-device because link gets triggered


I've got the following problem with a menu on a responive website:

I created a html menu which has ul/li-structure which contains links as category-names

<ul>
    <li>
       <a href="linkToCat">maincat1</a>
        <ul>
            <li>
                <a href="linkToCat">subcat1.1</a>
                <ul>
                    //deeper category stucture
                </ul>
            </li>
        </ul>
    </li>
    <li>
       <a href="linkToCat">maincat2</a>
        <ul>
            <li>
                <a href="linkToCat">subcat2.1</a>
            </li>
            <li>
                <a href="linkToCat">subcat2.2</a>
            </li>
        </ul>
    </li>
</ul>
<style>
    li > ul {
        display:none;
    } 

    li:hover > ul {
        display:block;
    } 
</style>

I am only showing the maincats at the beginning and the the subcats open on hover as shown in this JSFiddle.

Everything just works fine on desktop. The problem is that it doesnt work on touch-devices (e.g. smartphones/tablets) as soon as I use links as the names of the categories (Which I need to do).

Is there any way to make a menu that works with hover on desktop and that is still useable on touch-devices when using links as categorynames?

I got no problem about using JavaScript or jQuery to solve this problem. Normally I am using a responsive design with a extram menu for smartphones or other mobile devices. Therefor I am using @media screen. This sadly doesn't work with all touch devices like for example bigger tablets like some iPads or the microsoft surface.

Thank you for your answers and hints.

Edit: The problem is the <a href> The link always get triggert and so the menu doesn't open when i click on a category. I now also have added links to the categorynames in the JSFiddle


Solution

  • Workaround with doubleclick for touch-devices

    I now found a solution for my problem by adding the following JavaScript

    The idea of how to dectect if the device is a touch device is based on this answer.

    $(document).ready(function(){
        //added for surface
        window.USER_IS_TOUCHING = false;
        window.addEventListener('touchstart', function onFirstTouch() {
            window.USER_IS_TOUCHING = true;
            // we only need to know once that a human touched the screen, so we can stop listening now
            window.removeEventListener('touchstart', onFirstTouch, false);
        }, false);
    
        function isTouchDevice() {
          return 'ontouchstart' in window        // works on most browsers 
              || navigator.maxTouchPoints;       // works on IE10/11 and Surface
        };
        $('ul > li > a').click(function(e){
            var target = $(e.target);
            var parent = target.parent(); // the li
            if(isTouchDevice() || window.USER_IS_TOUCHING){
                if(target.hasClass("active")){
                    //run default action of the link
                }
                else{
                    e.preventDefault();
                    //remove class active from all links
                    $('ul > li > a.active').removeClass('active');
                    //set class active to current link
                    target.addClass("active");
                    parent.addClass("active");
                }
            }
        });
        $('ul > li').click(function(e){
            //remove class active from all links if li was clicked
            if (e.target == this){
                $(".active").removeClass('active');
            }
        });
    });
    

    And the following css

    .active > ul >li{
        display:block;
    }
    

    Now the first click of a touch device opens the submenu while a doubleclick runs the default action of the link.

    I have tested this solution on an android smartphone & tablet and also on iphone & ipad. I havn't had the possibility to test it on a touchlaptop or microsoft surface yet. If anyone has: feel free to write a comment

    Here's an example JsFiddle

    Or you can also try it out here:

    $(document).ready(function(){
    	window.USER_IS_TOUCHING = false;
    	window.addEventListener('touchstart', function onFirstTouch() {
        window.USER_IS_TOUCHING = true;
    	 	// we only need to know once that a human touched the screen, so we can stop listening now
      	window.removeEventListener('touchstart', onFirstTouch, false);
    	}, false);
    
      function isTouchDevice() {
        return 'ontouchstart' in window        // works on most browsers 
            || navigator.maxTouchPoints;       // works on IE10/11 and Surface
      };
      $('ul > li > a').click(function(e){
          var target = $(e.target);
          var parent = target.parent(); // the li
          if(isTouchDevice() || window.USER_IS_TOUCHING){
              if(target.hasClass("active")){
                  //run default action of the link
              }
              else{
                  e.preventDefault();
                  //remove class active from all links
                  $('ul > li > a.active').removeClass('active');
                  //set class active to current link
                  target.addClass("active");
                  parent.addClass("active");
              }
          }
      });
      $('ul > li').click(function(e){
        //remove class active from all links if li was clicked
        if (e.target == this){
          $(".active").removeClass('active');
        }
      });
    });
    li > ul {
      display:none;
    } 
    
    li:hover > ul {
      display:block;
    } 
    
    .active > ul >li{
      display:block;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <ul>
            <li>
                <a href="stackoverflow.com">maincat1</a>
                <ul>
                    <li>
                        <a href="stackoverflow.com">subcat1.1</a>
                        <ul>
                            <li>
                                subcat1.1.1
                            </li>
                            <li>
                                subcat1.1.2
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
            <li>
                <a href="stackoverflow.com"> maincat2</a>
                <ul>
                    <li>
                        subcat2.1
                    </li>
                    <li>
                        subcat2.2
                    </li>
                    <li>
                        subcat2.3
                         <ul>
                            <li>
                                subcat2.3.1
                            </li>
                            <li>
                                subcat2.3.2
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul>