Search code examples
javascripthtmlcssclicktargeting

Sub-menu does not hide when it is not the click target


I've been struggling for days fixing this simple problem. The sub-menu of one of my navigation menu's tabs is supposed to be closing when other elements are being clicked but it won't close.

var query = document.querySelector.bind(document);

query('.drop-down').onclick = function() {
  query('.sub-menu').classList.toggle('nav-show');
}

window.onclick = function(e) {
  if (query('.sub-menu').style.display == 'block') {
    if (e.target != query('.sub-menu')) {
      query('.sub-menu').classList.remove('nav-show');
    }
  }
}
.menu {
  display: table;
  background: #f9f8f8;
  padding: 0;
  margin: 5em auto;
  border-radius: 0.5em;
  box-shadow: 0 3px 8px -5px rgba(0, 0, 0, 0.8);
}
.menu > li {
  display: table-cell;
  width: 100px;
  list-style: none;
  text-align: center;
}
.menu > li.drop-down {
  position: relative;
}
.menu > li > a {
  display: block;
  text-decoration: none;
  color: #9c9c9f;
  padding: .8em;
  font: 1em Arial, sans-serif;
}
.menu > li > a:hover {
  color: #757579;
}
.sub-menu {
  position: absolute;
  padding: 0;
  top: 100%;
  right: 0;
  left: 0;
  background: #f9f8f8;
  border: 1px solid #f1f1f1;
  box-shadow: 0 3px 8px -5px rgba(0, 0, 0, 0.8);
  display: none;
}
.sub-menu.nav-show {
  display: block;
}
.sub-menu > li {
  list-style: none;
  display: block;
  text-align: center;
}
.sub-menu > li:not(:first-child) {
  border-top: 1px solid #f1f1f1;
}
.sub-menu > li > a {
  text-decoration: none;
  display: block;
  font: 0.9em Arial, sans-serif;
  color: #9c9c9f;
  padding: 1em 0;
}
.sub-menu > li > a:hover {
  background: white;
}
<ul class="menu">
  <li><a href="" onclick="return false">Home</a>
  </li>
  <li class="drop-down"><a href="" onclick="return false">Projects</a>
    <ul class="sub-menu">
      <li><a href="/">HTML</a>
      </li>
      <li><a href="/">CSS3</a>
      </li>
      <li><a href="/">JavaScript</a>
      </li>
      <li><a href="/">PHP</a>
      </li>
    </ul>
  </li>
  <li><a href="" onclick="return false">Forums</a>
  </li>
  <li><a href="" onclick="return false">Blog</a>
  </li>
</ul>

Can someone help me fix this problem?


Solution

  • You will need the stopPropagation() method to prevent any parent event handlers from being executed. See w3 wiki for more information.

    Here's a quick example with jQuery:

    $(".drop-down").click(function(e) {
      e.stopPropagation();
      $('.sub-menu').toggleClass('nav-show');
    });
    
    $(document).click(function(e) {
        $('.sub-menu').removeClass('nav-show');
    });
    .menu {
      display: table;
      background: #f9f8f8;
      padding: 0;
      margin: 5em auto;
      border-radius: 0.5em;
      box-shadow: 0 3px 8px -5px rgba(0, 0, 0, 0.8);
    }
    .menu > li {
      display: table-cell;
      width: 100px;
      list-style: none;
      text-align: center;
    }
    .menu > li.drop-down {
      position: relative;
    }
    .menu > li > a {
      display: block;
      text-decoration: none;
      color: #9c9c9f;
      padding: .8em;
      font: 1em Arial, sans-serif;
    }
    .menu > li > a:hover {
      color: #757579;
    }
    .sub-menu {
      position: absolute;
      padding: 0;
      top: 100%;
      right: 0;
      left: 0;
      background: #f9f8f8;
      border: 1px solid #f1f1f1;
      box-shadow: 0 3px 8px -5px rgba(0, 0, 0, 0.8);
      display: none;
    }
    .sub-menu.nav-show {
      display: block;
    }
    .sub-menu > li {
      list-style: none;
      display: block;
      text-align: center;
    }
    .sub-menu > li:not(:first-child) {
      border-top: 1px solid #f1f1f1;
    }
    .sub-menu > li > a {
      text-decoration: none;
      display: block;
      font: 0.9em Arial, sans-serif;
      color: #9c9c9f;
      padding: 1em 0;
    }
    .sub-menu > li > a:hover {
      background: white;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <ul class="menu">
      <li><a href="" onclick="return false">Home</a>
      </li>
      <li class="drop-down"><a href="" onclick="return false">Projects</a>
        <ul class="sub-menu">
          <li><a href="/">HTML</a>
          </li>
          <li><a href="/">CSS3</a>
          </li>
          <li><a href="/">JavaScript</a>
          </li>
          <li><a href="/">PHP</a>
          </li>
        </ul>
      </li>
      <li><a href="" onclick="return false">Forums</a>
      </li>
      <li><a href="" onclick="return false">Blog</a>
      </li>
    </ul>