У меня есть такой вот класс для попапов, который я переношу из проекта в проект.
Недавно добавил возможность подписываться на события, например, показа или скрытия попапа, чтобы выполнять какие-то действия.
Но я подумал, что такую систему можно было бы использовать в других классах, например, в аккордеоне, в табах и пр. Не хотелось бы каждому классу писать одни и те же методы .on, .off, .emit и т. д. и поле .eventListeners.
Как бы всё это красиво организовать, выделить в отдельный модуль и подключать во всех классах? Как назвать такой модуль?
class Popup {
static active = null;
constructor($element) {
this.$container = $element;
this.$triggers = document.querySelectorAll(`[data-popup=${$element.id}]`);
this.$closer = $element.querySelector(".popup__closer");
this.eventListeners = {};
this.listenEvents();
}
listenEvents() {
for (const trigger of this.$triggers) {
trigger.addEventListener("click", this.onTriggerClick.bind(this));
}
this.$closer.addEventListener("click", this.hide.bind(this));
this.$container.addEventListener("click", this.onContainerClick.bind(this));
document.addEventListener("keydown", this.onKeyDown.bind(this));
}
show() {
this.$container.classList.add("active");
Popup.active = this;
this.emit("show");
}
hide() {
this.$container.classList.remove("active");
Popup.active = null;
this.emit("hide");
}
emit(eventName) {
if (!this.eventListeners[eventName]) {
this.eventListeners[eventName] = new Set();
}
for (const eventHandler of this.eventListeners[eventName]) {
eventHandler(this);
}
}
on(eventName, eventHandler) {
if (!this.eventListeners[eventName]) {
this.eventListeners[eventName] = new Set();
}
this.eventListeners[eventName].add(eventHandler);
}
off(eventName, eventHandler) {
if (!this.eventListeners[eventName]) {
return;
}
this.eventListeners[eventName].delete(eventHandler);
}
onTriggerClick({currentTarget}) {
this.$trigger = currentTarget;
this.show();
}
onContainerClick({target, currentTarget}) {
if (target === currentTarget) {
this.hide();
}
}
onKeyDown({key}) {
if (!Popup.active || key !== "Escape") {
return;
}
Popup.active.hide();
}
}
export default Popup;
Пример использования:
const feedbackPopup = new Popup(document.getElementById("feedbackPopup"));
feedbackPopup.on("show", popup => {
// ...
});