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

    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)
    Ответ написан
    Комментировать
  • Почему три варианта одной и той же функции работают по-разном?

    director-rentv
    @director-rentv
    Frontend-разработчик
    Насколько я вижу, в первом варианте, lookupDismissalReasonsByGroup - это функция (1), которая возвращает функцию (2), которая возвращает функцию (3)

    И в массиве объектов поле options - это результат вызова (1), т.е. (2)
    Затем в строке
    const options = this.field.options(this.currentValue, this.value)
    вызывается (2) и в options записывается (3)

    В случае же второго варианта lookupDismissalReasonsByGroup получается совершенно такая же по вложенности функций. Но в массиве объектов теперь в поле options - не результат вызова (1), а другая функция (0), которая внутри себя вызывает (1) и возвращает её результат (2)
    И получается, что далее в
    const options = this.field.options(this.currentValue, this.value)
    вызывается (0) и в options записывается (2), т.е. не та же функция, как в первом примере
    Ответ написан
    Комментировать
  • Как сделать сквозную нумерацию неупорядоченных списков?

    director-rentv
    @director-rentv
    Frontend-разработчик
    В этом участке кода получилось добиться сквозной нумерации таким кодом:
    ul:first-of-type {
      counter-reset: cnt 0;
    }
    
    li {
      counter-increment: cnt 1;
    }
    
    li::before {
      content: counter(cnt) ". ";
    }
    Ответ написан
    3 комментария