Close menu when click / touch outside javascript

拥有回忆 提交于 2021-01-29 01:45:28

问题


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


回答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...




回答2:


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


来源:https://stackoverflow.com/questions/38358950/close-menu-when-click-touch-outside-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!