1

A css / javascript trigger menu doesnt close when someone click or touch outside.

You have to press the menu button to close it. I tried some jquery functions like:

$('#menucontainer').click(function(event) {
    event.stopPropagation();
});

But this does not work. Here I made a simple jsfiddle with all related code:

https://jsfiddle.net/4sgf84jL/

I would appreciate some help. Thank you

Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46
labu77
  • 605
  • 1
  • 9
  • 30
  • 2
    Possible duplicate of [How to detect a click outside an element?](http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element) – Martin Hučko Jul 13 '16 at 18:17
  • I tried just this solution. My nav is complete different. – labu77 Jul 13 '16 at 18:20
  • It doesn't matter the solution would be the same, you need to add a click listener on the document, and check the `event.target` if it is anything that is not your menu or elements inside your menu you close it. The [second answer on the linked duplicate](http://stackoverflow.com/a/3028037/560593) is the one you want to use, substituting your menu id/class with the one the answer uses – Patrick Evans Jul 13 '16 at 18:21
  • if you want to use `jQuery` then here is updated [jsfiddle](https://jsfiddle.net/webdevanuj/4sgf84jL/2/) – webdevanuj Jul 13 '16 at 18:36

2 Answers2

1

When you open the menu, you should listen for clicks on document. Then when the document is clicked you close the popup (and you remove the listener on the document as well).

PS: keep your listener on the menu-container as well ;)

Here is an example you can use (I edited your fiddle) :

(function(){
 //Remember if the menu is opened or not
  var menuOpened = false;
 var menuElement = document.getElementById('menu_control');
  var menuContainer = document.getElementById('menu-standard');
  
  // Add click listener on menu icon
  menuElement.addEventListener('click', onMenu_click);
  
   // Add click listener on menu 
  menuContainer.addEventListener('click', onMenuContainer_click);
  
  function toggleMenu(){
   menuOpened = !menuOpened;
  
   if (menuOpened){
     menuContainer.className += ' show_menu';
      document.addEventListener('click', onDoc_click);
    }
    else{
     menuContainer.className = menuContainer.className.replace('show_menu', '').trim();
      document.removeEventListener('click', onDoc_click);
    }
  }
  
  function onMenu_click(domEvent){
   domEvent.stopPropagation();
    toggleMenu();
  }
  
  function onDoc_click(domEvent){
   domEvent.stopPropagation();
    toggleMenu();
  }
  
  function onMenuContainer_click(domEvent){
   domEvent.stopPropagation();
  }
})();
.nav,
.menu_control{font-size:16px;line-height:23px;}
.nav{display:none;position:relative;width:219px;height:0;top:7px;list-style:none;z-index:9;background-color:#666;color:#fff}
.nav .sub-menu{list-style:none;padding-left:14px;}
.nav .sub-menu li{width:192px;background-color:#666;}
.nav .sub-menu .current-menu-item > a{background-color:#666;}
.nav a,
.show_menu{display:block;}
.nav a{color:#fff;padding:7px 14px;}
.nav a:hover{color:white;background-color:#999;}
.nav .current-menu-item > a{color:#fff;background-color:#666;cursor:text;}
.nav li{background-color:#666;}
.menu_control{display:block;color:#111111;cursor:pointer;margin:7px -27px 0 -27px;padding-right:27px;padding-left:27px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}
.navicon-line{width:32px;height:4px;border-radius:1px;margin-bottom:5px;background-color:#333;}
<span id="menu_control" class="menu_control">
    <div class="navicon-line"></div>
     <div class="navicon-line"></div>
    <div class="navicon-line"></div>
</span>
        
<ul id="menu-standard" class="nav">
<li id="menu-item"><a href="/">Home</a></li>
<li id="menu-item"><a href="#">test</a>
<ul class="sub-menu">
  <li id="menu-item"><a href="#">test</a></li>
  <li id="menu-item"><a href="#">test</a></li>
  <li id="menu-item"><a href="#">test</a></li>
  <li id="menu-item"><a href="#">test</a></li>
 </ul>
</li>
<li id="menu-item"><a href="#">test</a></li>
</ul>
<br />
Content Content  Content 

It's not the best way to do it because you add several click listeners... You could have just one listener on the doc and do different things depending on the target of the event...

R. Foubert
  • 633
  • 4
  • 8
  • I tried this already. I found this here: https://www.developphp.com/video/JavaScript/Click-Outside-Close-Menu-Box-Tutorial but it was not working for me. – labu77 Jul 13 '16 at 18:20
  • Can you really have several menu icons ? Because you loop through several domObjects that you get using the className... You can not do it with an id ? – R. Foubert Jul 13 '16 at 18:23
  • a ID is also possible – labu77 Jul 13 '16 at 18:25
  • I updated my answer using your example. Tell me if it helps ;) – R. Foubert Jul 13 '16 at 18:42
  • Thank you very much. Your solution works great on mobile and desc. gaetanoM provided also a working solution. Which solution is the best in your opinion with regard to support devices? – labu77 Jul 13 '16 at 20:20
  • You're welcome ;) I think mine is a little bit cleaner in my opinion... It's easier to maintain and to understand for someone who don't know the code... But it's up to you to chose the one which fit your needs ;) – R. Foubert Jul 14 '16 at 09:35
0

Thanks to finding-closest-element-without-jquery my solution is based on:

  • window.onload: try to insert always your code in such handler to be sure all the elements are already loaded and so ready for your code
  • to test if an element has a class use menu.classList.contains('show_menu')
  • to add/remove classes use menu.classList.remove('show_menu'); or menu.classList.add('show_menu');
  • add a listener for the whole document so that if you clicked ouside your menu you can remove the corresponding show_menu if added

My snippet:

function closest(el, selector) {
  var matchesFn;

  // find vendor prefix
  ['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
    if (typeof document.body[fn] == 'function') {
      matchesFn = fn;
      return true;
    }
    return false;
  })

  var parent;

  // traverse parents
  while (el) {
    parent = el.parentElement;
    if (parent && parent[matchesFn](selector)) {
      return parent;
    }
    el = parent;
  }

  return null;
}
window.onload = function() {
  (function(){
    document.addEventListener('click', function(e) {
      if (e.target.className.indexOf('menu_control') == -1 &&
          e.target.className.indexOf('navicon-line') == -1 &&
          closest(e.target, '#menu-standard.nav') == null) {
        // menu-standard
        document.getElementById('menu-standard').classList.remove('show_menu');
      }
    }, false);
    var classes = document.getElementsByClassName('menu_control');
    for (i = 0; i < classes.length; i++) {
      classes[i].onclick = function() {
        var menu = this.nextElementSibling;
        if (menu.classList.contains('show_menu'))
          menu.classList.remove('show_menu');
        else
          menu.classList.add('show_menu');
      };
    }
  })();
}
.nav,
.menu_control{font-size:16px;line-height:23px;}
.nav{display:none;position:relative;width:219px;height:0;top:7px;list-style:none;z-index:9;background-color:#666;color:#fff}
.nav .sub-menu{list-style:none;padding-left:14px;}
.nav .sub-menu li{width:192px;background-color:#666;}
.nav .sub-menu .current-menu-item > a{background-color:#666;}
.nav a,
.show_menu{display:block;}
.nav a{color:#fff;padding:7px 14px;}
.nav a:hover{color:white;background-color:#999;}
.nav .current-menu-item > a{color:#fff;background-color:#666;cursor:text;}
.nav li{background-color:#666;}
.menu_control{display:block;color:#111111;cursor:pointer;margin:7px -27px 0 -27px;padding-right:27px;padding-left:27px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}
.navicon-line{width:32px;height:4px;border-radius:1px;margin-bottom:5px;background-color:#333;}
<span class="menu_control">
    <div class="navicon-line"></div>
     <div class="navicon-line"></div>
    <div class="navicon-line"></div>
</span>

<ul id="menu-standard" class="nav">
    <li id="menu-item"><a href="/">Home</a></li>
    <li id="menu-item"><a href="#">test</a>
        <ul class="sub-menu">
            <li id="menu-item"><a href="#">test</a></li>
            <li id="menu-item"><a href="#">test</a></li>
            <li id="menu-item"><a href="#">test</a></li>
            <li id="menu-item"><a href="#">test</a></li>
        </ul>
    </li>
    <li id="menu-item"><a href="#">test</a></li>
</ul>
<br />
Content Content  Content
Community
  • 1
  • 1
gaetanoM
  • 41,594
  • 6
  • 42
  • 61
  • Thank you. It works like a charm. R. Foubert provided also a working solution. Which solution is the best in your opinion with regard to support devices? – labu77 Jul 13 '16 at 20:21