Задать вопрос
@ArthurOrlov

Выпадающее меню сворачивается только на главной странице, как исправить?

Приветствую!
На сайте есть выпадающее меню, переключающее филиал.
При клике на любое свободное место меню сворачивается обратно, но работает это только на главной странице. Не могу понять в чем проблема, и как исправить, чтобы работало на всех страницах. Ошибку никакую не выдает

<div class="dropdown">
                <button onclick="myFunction()" class="dropbtn">
                  <img class="dropbtn-img" src="./img/yellow_mark.png" alt="mark">
                  Обнинск
                  <img class="dropbtn-arrow" src="./img/drop-arrow.png" alt="arrow">
                </button>
                <div id="myDropdown" class="dropdown-content">
                  <a href="mytishchi.html">Мытищи</a>
                </div>
              </div>

function myFunction() {
  document.getElementById("myDropdown").classList.toggle("show");
}

function closeDrop() {
  document.addEventListener('click', function (drop) {
    if (!drop.target.matches('.dropbtn')) {
      let dropdowns = document.getElementsByClassName("dropdown-content");
      for (let i = 0; i < dropdowns.length; i++) {
        let openDropdown = dropdowns[i];
        if (drop.target.closest('.dropdown') !== openDropdown && drop.target !== openDropdown) {
          openDropdown.classList.remove('show');
        }
      }
    }
  });
};
closeDrop();
  • Вопрос задан
  • 86 просмотров
Подписаться 1 Простой 2 комментария
Пригласить эксперта
Ответы на вопрос 1
director-rentv
@director-rentv
Frontend-разработчик
Вообще очень неочевидный код, как будто бы содержит логические ошибки

Касательно функции-обработчика click для документа (подписывается в closeDrop):
1. первый if - для упрощения восприятия стоит инвертировать условие и сделать ранний возврат
т.е. вместоif (!drop.target.matches('.dropbtn')) сделать if (drop.target.matches('.dropbtn')) return; и убрать лишние фигурные скобки

2. Т.к. насколько я понимаю, в этой функции нас интересуют исключительно открытые выпадашки - чтобы их закрыть, то мы можем выбрать сразу только открытые - заменив
let dropdowns = document.getElementsByClassName("dropdown-content");
на
let dropdowns = document.querySelectorAll(".dropdown-content.show");


3. В if в цикле по элементам происходит поиск родителя с классом .dropdown (вызов .closest('.dropdown')), т.е. самого верхнего элемента - общего родителя кнопки и самой выпадашки, но сравнивается он с одной из выпадашек (openDropdown, который имеет класс .dropdown-content.show, но не .dropdown), т.е это условие всегда истинно, т.к. в данной вёрстке никогда drop.target.closest('.dropdown') не будет равен openDropdown. Так же сюда же, какой смысл делать .closest от таргета клика на каждом шаге цикла, если он в течение всей функции будет одинаков?
Далее проверяется drop.target !== openDropdown, который будет всегда true, кроме случая клика строго по .dropdown-content.show и ни в какого его потомка

4. Опять же, если судить по HTML-коду, то выпадашка уникальна - т.к. указан id (который обязан быть уникален в документе), т.е. много таких вставлять нельзя. Но в обработчике click выпадашки как будто бы во множественном числе упоминаются. Тут нужно уже решить, как правильно должно быть - либо от id и привязки к нему избавляться, либо незачем городить перебор элементов в цикле в обработчике click

Теперь, как я бы написал решение на голом html+js:
Переименовал классы для понятности и убрал id
<div class="dropdown-wrapper">
  <button class="dropdown-open-button">
    <img class="dropdown-open-button-img" src="./img/yellow_mark.png" alt="mark">
    Обнинск
    <img class="dropdown-open-button-arrow" src="./img/drop-arrow.png" alt="arrow">
  </button>
  <div class="dropdown-content">
    <a href="mytishchi.html">Мытищи</a>
  </div>
</div>


И здесь реализовал бы скрипт иначе, отвязавшись от id, и сделав поддержку множественных выпадашек
function hydrateDropdown(wrapperEl) {
  // получив элемент-обёртку, найдём внутри него кнопку и саму выпадашку
  const buttonEl = wrapperEl.querySelector('button.dropdown-open-button')
  const contentEl = wrapperEl.querySelector('div.dropdown-content')
  
  function onButtonClick() {
    contentEl?.classList.toggle('show')
  }
  
  // повесим на кнопку обработчик, который будет показывать/скрывать выпадашку
  buttonEl?.addEventListener('click', onButtonClick)

  // и реализуем простой обработчик клика по документу
  function onDocumentClick(event){
    // проверим, находится ли кликнутый элемент внутри именно текущей обёртки
    if (event.target?.closest('.dropdown-wrapper') === wrapperEl) return // если да, то ничего не делаем
    contentEl?.classList.remove('show') //если нет, то скрываем текущую выпадашку
  }

  document.addEventListener('click', onDocumentClick)
}

// возьмём все обёртки из документа и добавим к ним функциональность выпадашек
Array.from(document.querySelectorAll('.dropdown-wrapper')).forEach(hydrateDropdown)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы