Написал скрипт для создания выпадающего меню, с использованием БЭМ нотации. И с использованием css анимации. При этом внешний вид каждого меню можно сделать индивидуальным. Но я понимаю, что вот так вот сразу все сделать хорошо не получится. Поэтому пожалуйста, укажите узкие места.
class DropdownMenu {
constructor(menu) {
this._menu = menu; // Само меню
this._button = this._menu.querySelector('.js-dropdown__toggler'); // Кнопка открытия меню
this._baseClass = this._menu.dataset.baseClass; // Основной класс блока. Для создания правильного класса с --open
this._isOpen = false; // Открыто меню или нет
this._button.addEventListener('click', this._toggle.bind(this));
// Удаление класса закрывающей анимации
this._menu.addEventListener('animationend', () => {
this._menu.classList.remove('js-dropdown--close', `${this._baseClass}--close`);
});
}
_toggle(event) {
if(event) {
event.preventDefault();
event.stopPropagation();
}
if(this._isOpen) {
this.close();
} else {
// Если есть открытое меню, закрываем его
if(DropdownMenu.openedMenu) {
DropdownMenu.openedMenu.close();
}
this.open();
}
}
open() {
this._menu.classList.add('js-dropdown--open');
if(this._baseClass) {
this._menu.classList.add(`${this._baseClass}--open`);
}
this._isOpen = true;
// Сохраняем открытое меню
DropdownMenu.openedMenu = this;
}
close() {
this._menu.classList.add('js-dropdown--close');
this._menu.classList.remove('js-dropdown--open');
if(this._baseClass) {
this._menu.classList.add(`${this._baseClass}--close`);
this._menu.classList.remove(`${this._baseClass}--open`);
}
this._isOpen = false;
DropdownMenu.openedMenu = null;
}
static init(menu) {
if(!DropdownMenu._initialized) {
DropdownMenu._initialized = true;
// Закрытие меню при клике вне меню
document.addEventListener('click', ({target}) => {
if(DropdownMenu.openedMenu && !target.closest('.js-dropdown__inner')) {
DropdownMenu.openedMenu.close();
}
});
}
return new DropdownMenu(menu);
}
}
const dropdownMenus = document.querySelectorAll('.js-dropdown');
if(dropdownMenus) {
[...dropdownMenus].forEach((menu) => {
DropdownMenu.init(menu);
});
}