Преобразовать код под Vue.js

Рейтинг: 0Ответов: 1Опубликовано: 29.03.2023

У меня есть функция на JQuery:

$('.dropdown-toggle').click(function(e){
    e.preventDefault();
    $(this).parent().toggleClass('open');
    $(document).mouseup(function(e){
        let item = $(".dropdown.open");
        if (item.has(e.target).length === 0){
            item.removeClass('open');
        }
    })
});

Можно ли ее как-то преобразовать чтобы она работала на Vue.js 3 ?

Я думал в верстке на каждый div[.dropdown-toggle] сделать типа

<div class="dropdown-toggle" @click="function...">...

На так как таких блоков много, я думаю это не лучшее решение.

Буду очень благодарен за помощь!

UPD. Вот что у меня получилось:

    mounted() {
        let dropdowns = this.$el.querySelectorAll('.dropdown-toggle');
        dropdowns.forEach(dropdown => {
            dropdown.addEventListener('click', function(event) {
                let parent = event.target.closest('.dropdown');
                parent.classList.toggle("open");
            });
        });
    }

Но не могу понять как сделать чтобы класс open удалялся если кликнуть в любом другом месте страницы...

UPD. получилось сделать таким образом

        window.addEventListener('mouseup', event => {
            const opened_dropdown = this.$el.querySelector('.dropdown.open');
            if (opened_dropdown) {
                if ( !opened_dropdown.contains(event.target) ) {
                    opened_dropdown.classList.remove("open");
                }
            }
        }, false);

Огромная просьба - если видите какие-то недостатки такого решения, или есть более изящный вариант, напишите в ответ.

Ответы

▲ 2

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

mounted() {
  // Add click listener to document
  document.addEventListener('click', this.closeDropdowns);

  // Add click listener to each .dropdown-toggle
  let dropdowns = this.$el.querySelectorAll('.dropdown-toggle');
  dropdowns.forEach(dropdown => {
    dropdown.addEventListener('click', event => {
      let parent = event.target.closest('.dropdown');
      parent.classList.toggle("open");
    });
  });
},
methods: {
  closeDropdowns(event) {
    // Check if click was inside a dropdown
    let dropdowns = this.$el.querySelectorAll('.dropdown');
    dropdowns.forEach(dropdown => {
      if (!dropdown.contains(event.target)) {
        dropdown.classList.remove('open');
      }
    });
  }
},
beforeUnmount() {
  // Remove click listener from document when component is removed
  document.removeEventListener('click', this.closeDropdowns);
}

Что касаемо вашего обновленного кода - лучше использовать document вместо window, потому что document является родительским элементом всех элементов на странице, что обеспечивает более точную проверку клика вне списка. Ну и лучше использовать mousedown вместо mouseup, потому что mousedown происходит раньше, чем mouseup, что в свою очередь может снизить мерцание интерфейса.

Используя стрелочные функции:

mounted() {
  let dropdowns = document.querySelectorAll('.dropdown-toggle');
  dropdowns.forEach(dropdown => {
    let clickHandler = event => {
      let parent = event.target.closest('.dropdown');
      parent.classList.toggle("open");
    };
    dropdown.addEventListener('click', clickHandler);
  });
},
beforeUnmount() {
  let dropdowns = document.querySelectorAll('.dropdown-toggle');
  dropdowns.forEach(dropdown => {
    let clickHandler = event => {
      let parent = event.target.closest('.dropdown');
      parent.classList.toggle("open");
    };
    dropdown.removeEventListener('click', clickHandler);
  });
}

Сохраняем ссылку на анонимную функцию, переданную в addEventListener в переменной, чтобы далее использовать ее в removeEventListener.