Как сделать кастомную систему событий?

У меня есть такой вот класс для попапов, который я переношу из проекта в проект.
Недавно добавил возможность подписываться на события, например, показа или скрытия попапа, чтобы выполнять какие-то действия.

Но я подумал, что такую систему можно было бы использовать в других классах, например, в аккордеоне, в табах и пр. Не хотелось бы каждому классу писать одни и те же методы .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 => {
    // ...
});
  • Вопрос задан
  • 161 просмотр
Решения вопроса 2
sergiks
@sergiks Куратор тега JavaScript
♬♬
Trait'ов в JavaScript не завезли, к сожалению. Поэтому только наследоваться от этого класса, наверное.

Первый из принципов SOLID требует, чтобы у класса было единственное предназначение. Не стоит делать из класса швейцарский набор зубочисток.

Для событийного вдохновения посмотрите библиотеку RxJS – займёт на какое-то время )
Ответ написан
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Подключайте как миксин
const Eventable = (Base) => class extends Base {
  // Здесь ваши общие свойства и методы для событий
};

const Popup = Eventable(
  class {
    // Здесь основная часть класса
  },
);
или
class Popup extends Eventable(
  class {
    // Здесь основная часть класса
  },
) { };
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы