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

Здравствуйте! <br/> У меня есть выпадающее меню на сайте, которое используется для переключения между филиалами. При клике на любое свободное место меню сворачивается обратно, однако эта функция работает только на главной странице. Не могу понять, в чем заключается проблема, и как сделать так, чтобы она функционировала на всех страницах. Ошибок в консоли не возникает. <br/> <br/> <pre><code>&lt;div class="dropdown"&gt;
                &lt;button onclick="myFunction()" class="dropbtn"&gt;
                  &lt;img class="dropbtn-img" src="./img/yellow_mark.png" alt="mark"&gt;
                  Обнинск
                  &lt;img class="dropbtn-arrow" src="./img/drop-arrow.png" alt="arrow"&gt;
                &lt;/button&gt;
                &lt;div id="myDropdown" class="dropdown-content"&gt;
                  &lt;a href="mytishchi.html"&gt;Мытищи&lt;/a&gt;
                &lt;/div&gt;
              &lt;/div&gt;

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 &lt; dropdowns.length; i++) {
        let openDropdown = dropdowns[i];
        if (drop.target.closest('.dropdown') !== openDropdown &amp;&amp; drop.target !== openDropdown) {
          openDropdown.classList.remove('show');
        }
      }
    }
  });
};
closeDrop();</code></pre>
  • 10 января 2025 г. 14:34
Ответы на вопрос 2
Проблема, с которой вы сталкиваетесь, может быть связана с тем, как обрабатываются события клика на различных страницах. Давайте разберемся с тем, что может быть не так и как это исправить.

1. **Проблема с областью видимости JavaScript**: Убедитесь, что ваш JavaScript-код действительно загружается и выполняется на всех страницах сайта. Если у вас есть разные структуры HTML на различных страницах или если скрипт загружается асинхронно, это может вызывать проблемы.

2. **Порядок загрузки**: Убедитесь, что ваша функция `closeDrop` вызывается после того, как все элементы DOM полностью загружены. Например, вставьте код вашего JavaScript в `window.onload` или используйте `DOMContentLoaded`:

   ```javascript
   document.addEventListener('DOMContentLoaded', function () {
       closeDrop();
   });
   ```

3. **Проверка уникальности идентификаторов**: Убедитесь, что на других страницах нет других элементов с тем же идентификатором `myDropdown`, поскольку функция `getElementById` ищет только один элемент. Возможно, другие страницы используют аналогичные идентификаторы.

4. **Настройка CSS**: Убедитесь, что ваши CSS-правила для `.show` и `.dropdown-content` правильно применяются на всех страницах и не перекрываются другими стилями.

Пример исправленного кода с учетом вышеуказанных рекомендаций:

```html
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="style.css">
    <script>
        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');
                        }
                    }
                }
            });
        }

        document.addEventListener('DOMContentLoaded', function () {
            closeDrop();
        });
    </script>
</head>
<body>
    <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>
</body>
</html>
```

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

Касательно функции-обработчика 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)
Похожие вопросы