Показ и скрытие выпадающего меню на сайте

window.addEventListener("DOMContentLoaded", function() {

  let menu = this.document.querySelector(".header__nav"),
    wrapper = this.document.querySelector(".header__wrap"),
    menuText = this.document.querySelector(".header__text"),
    hamburger = this.document.querySelector(".header__hamburger"),
    dropdown = this.document.querySelector(".header__dropdown");


  let menuSet = function() {

    menu.addEventListener("click", function(e) {
      if (e.target == menu || e.target == menuText || e.target == hamburger || e.target == wrapper) {
        this.classList.toggle("header__nav_active");
        menuText.classList.toggle("header__text_active");
        hamburger.classList.toggle("header__hamburger_active");
        dropdown.classList.toggle("header__dropdown_active");
      }
    });

    /* document.body.addEventListener("click", (e) => {
        if (e.target !== dropdown) {
            menu.classList.remove("header__nav_active");
            menuText.classList.remove("header__text_active");
            hamburger.classList.remove("header__hamburger_active");
            dropdown.classList.remove("header__dropdown_active");
        }
    }); */
  };

  menuSet();
});
.header__text {
  margin-left: 10px;
  font-weight: 300;
  font-size: 16px;
  text-transform: uppercase;
  line-height: 20px;
  color: #fff;
}

.header__text_active {
  color: #336ABB;
}

.header__nav {
  width: 230px;
  position: relative;
  margin-top: 3px;
  margin-left: 133.5px;
  padding: 18px 20px;
  background-color: #336ABB;
  border-radius: 10px;
}

.header__nav_active {
  background-color: #fff;
  color: #336ABB;
}

.header__dropdown {
  display: none;
  position: absolute;
  top: 39px;
  left: 0;
  width: 169px;
  padding-top: 24px;
  padding-left: 22px;
  padding-bottom: 20px;
  background: #fff;
  border-radius: 0 10px 10px 10px;
  list-style-type: none;
  box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.25);
}

.header__dropdown_active {
  display: block;
}

.header__dropdown li {
  margin-bottom: 11px;
  color: #336ABB;
  font-weight: 300;
  font-size: 14px;
}

.header__dropdown li:nth-last-child(1) {
  margin-bottom: 0;
}

.header__wrap {
  display: flex;
  align-items: center;
}

.header__hamburger {
  display: flex;
  flex-direction: column;
  width: 19px;
  height: 10px;
}

.header__hamburger span {
  display: block;
  width: 100%;
  height: 2px;
  background-color: #fff;
}

.header__hamburger span:nth-child(2) {
  margin: 2px 0;
}

.header__hamburger_active span {
  background-color: #336ABB;
}

.header__hamburger_active span:nth-child(1) {
  transform: translateY(3px) rotate(-45deg);
  margin-bottom: -2px;
}

.header__hamburger_active span:nth-child(2) {
  display: none;
}

.header__hamburger_active span:nth-child(3) {
  transform: translateY(3px) rotate(45deg);
  margin-bottom: 6px;
}

.title {
  margin-top: 120px;
  padding-bottom: 56px;
  font-size: 87px;
  text-align: center;
}
<div class="header__menu header__menu_m-hidden">
  <nav class="header__nav">
    <div class="header__wrap">
      <div class="header__hamburger">
        <span></span>
        <span></span>
        <span></span>
      </div>
      <div class="header__text">меню</div>
    </div>
    <ul class="header__dropdown">
      <li>Каталог</li>
      <li>Аналитика</li>
    </ul>
  </nav>
</div>
<div class="title">Lorem ipsum</div>

На сайте есть меню, которое по задумке открывается/закрывается при клике на него и закрывается при клике вне меню.

  1. Часть кода работает. Но если не указывать "e.target" каждого вложенного элемента, то событие срабатывает только при клике по родителю "menu", но не по этим элементам. Как сделать иначе, что бы не перебирать по очереди все дочерние элементы?
  2. Как закрыть меню при клике в любом месте страницы, кроме самого выпадающего списка "dropdown" и вложенным в него "li"?

Ответы (1 шт):

Автор решения: ksa

Часть кода работает. Но если не указывать "e.target" каждого вложенного элемента, то событие срабатывает только при клике по родителю "menu", но не по этим элементам. Как сделать иначе, что бы не перебирать по очереди все дочерние элементы?

Просто проверяй родителя.

Как закрыть меню при клике в любом месте страницы, кроме самого выпадающего списка "dropdown" и вложенным в него "li"?

Такое можно сделать как в моем примере...

Так же как совет - не стоит вешать столько много классов типа active. Достаточно одного - на родителе. Остальные свойства можно определить относительно "активного" родителя. ;)

window.addEventListener("DOMContentLoaded", function() {
  const menu = this.document.querySelector(".header__nav"),
    wrapper = this.document.querySelector(".header__wrap"),
    menuText = this.document.querySelector(".header__text"),
    hamburger = this.document.querySelector(".header__hamburger"),
    dropdown = this.document.querySelector(".header__dropdown");
  const menuClose = function(e) {
    if (e.target.closest('.header__nav')) return
    menu.classList.remove("header__nav_active");
    menuText.classList.remove("header__text_active");
    hamburger.classList.remove("header__hamburger_active");
    dropdown.classList.remove("header__dropdown_active");
    document.removeEventListener("click", menuClose);
  };
  menu.addEventListener("click", function(e) {
    if (e.target.closest('.header__dropdown')) return
    this.classList.toggle("header__nav_active");
    menuText.classList.toggle("header__text_active");
    hamburger.classList.toggle("header__hamburger_active");
    dropdown.classList.toggle("header__dropdown_active");
    if (menu.classList.contains("header__nav_active")) 
      setTimeout(_ => document.addEventListener("click", menuClose));
    else 
      document.removeEventListener("click", menuClose);
  });
});
.header__text {
  margin-left: 10px;
  font-weight: 300;
  font-size: 16px;
  text-transform: uppercase;
  line-height: 20px;
  color: #fff;
}

.header__text_active {
  color: #336ABB;
}

.header__nav {
  width: 230px;
  position: relative;
  margin-top: 3px;
  margin-left: 133.5px;
  padding: 18px 20px;
  background-color: #336ABB;
  border-radius: 10px;
}

.header__nav_active {
  background-color: #fff;
  color: #336ABB;
}

.header__dropdown {
  display: none;
  position: absolute;
  top: 39px;
  left: 0;
  width: 169px;
  padding-top: 24px;
  padding-left: 22px;
  padding-bottom: 20px;
  background: #fff;
  border-radius: 0 10px 10px 10px;
  list-style-type: none;
  box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.25);
}

.header__dropdown_active {
  display: block;
}

.header__dropdown li {
  margin-bottom: 11px;
  color: #336ABB;
  font-weight: 300;
  font-size: 14px;
}

.header__dropdown li:nth-last-child(1) {
  margin-bottom: 0;
}

.header__wrap {
  display: flex;
  align-items: center;
}

.header__hamburger {
  display: flex;
  flex-direction: column;
  width: 19px;
  height: 10px;
}

.header__hamburger span {
  display: block;
  width: 100%;
  height: 2px;
  background-color: #fff;
}

.header__hamburger span:nth-child(2) {
  margin: 2px 0;
}

.header__hamburger_active span {
  background-color: #336ABB;
}

.header__hamburger_active span:nth-child(1) {
  transform: translateY(3px) rotate(-45deg);
  margin-bottom: -2px;
}

.header__hamburger_active span:nth-child(2) {
  display: none;
}

.header__hamburger_active span:nth-child(3) {
  transform: translateY(3px) rotate(45deg);
  margin-bottom: 6px;
}

.title {
  margin-top: 120px;
  padding-bottom: 56px;
  font-size: 87px;
  text-align: center;
}
<div class="header__menu header__menu_m-hidden">
  <nav class="header__nav">
    <div class="header__wrap">
      <div class="header__hamburger">
        <span></span>
        <span></span>
        <span></span>
      </div>
      <div class="header__text">меню</div>
    </div>
    <ul class="header__dropdown">
      <li>Каталог</li>
      <li>Аналитика</li>
    </ul>
  </nav>
</div>
<div class="title">Lorem ipsum</div>

→ Ссылка