Возможность программной генерации событий открывает нам путь к созданию кастомных событий - мы можем определять и вызывать произвольные события.
Например, у нас есть функция-конструктор Account, которая принимает коичество денег и создает условный денежный счет:
function Account(money) { _money = money; this.pay=function(sum){ if(_money >= sum){ _money -= sum; console.log(_money); } } }
В переменной _money хранится текущее количество денег на счете. С помощью функции pay условно тратим определенную сумму, если баланс позволяет. Но, допустим, нам надо как-то извещать
систему, что произошло списание со счета. С одно стороны, мы могли бы это делать непосредственно в методе pay - вызывать в методе console.log()
и
выводить на консоль какой-то текст. Но на момент написания этого кода мы можем быть не уверены, какой именно текст надо выводить на консоль. А может быть потребуется и не на консоль,
а в окне браузере. Или посылать извещение на определенный сетевой ресурс. А может наша функция-конструктор будет использоваться в Node.js, где может потребоваться какая-то другая обработка. Да и использовать нашу функцию-конструктор могут
совсем другие разработчики, у которых может быть собственно понимание того, что надо делать при списании средств. В любом случае мы сталкиваемся с многовариантностью, но
во всех этих ситуация главное, что нам надо сделать - уведомить систему, что произошло списание средств. И охватить все эти ситуации нам поможет определение собственных событий.
Для определения кастомных событий мы можем применять конструктор Event, в который передается название события. Так, рассмотрим следующую программу:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <button id="btn">Pay</button> <script> const button = document.getElementById("btn"); const myAcc = new Account(100); // условный денежный счет // устанавливаем обработчик события payment для всего документа document.addEventListener("payment", ()=>console.log("Payment succeeded!")); // по нажатию на кнопку выполняем метод pay button.addEventListener("click", ()=>myAcc.pay(50)); // конструктор объекта счета function Account(money) { _money = money; this.pay=function(sum){ if(_money >= sum){ _money -= sum; console.log(_money); const event = new Event("payment"); // определяем объект события document.dispatchEvent(event); // генерируем событие для всего документа } } } </script> </body> </html>
Основные моменты. В методе pay создаем объект Event, которое будет представлять событие "payment" (не важно, что такого события изначально не существует, мы сами его создаем). Затем генерируем это событие:
const event = new Event("payment"); // определяем объект события document.dispatchEvent(event); // генерируем событие для всего документа
Стоит отметить, что событие генерируется для всего документа: document.dispatchEvent(event)
, но это может быть
любой конкретный элемент веб-страницы.
Чтобы обработать это событие, подписываемся на него:
document.addEventListener("payment", ()=>console.log("Payment succeeded!"));
Опять же подписка на событие производится для всего документа. Обработчик события просто выводит строку на консоль.
По нажатию на кнопку вызываем метод pay объекта myAcc и тем самым генерируем событие "payment" (если на счете достаточно средств).
Для тестирования понажимаем на кнопку:
Также, как и в общем случае, мы можем получить объект подобное события в обработчике:
// получаем через параметр e объект события document.addEventListener("payment", (e)=>{ console.log(e.type); // payment console.log("Payment succeeded!"); });
Однако тип Event
хотя и может использоваться, но не очень подходит для определения кастомных событий. Например, что, если мы хотим передать в обработчик
события какую-то дополнительную информацию - сумму списания, текущий баланс или что-то еще? И для подобных случаев лучше использовать тип CustomEvent.
Так, изменим код JavaScript следующим образом:
const button = document.getElementById("btn"); document.addEventListener("payment", (e)=>{ console.log("Payment succeeded!"); console.log("Payment Sum:", e.detail.paymentSum); // получаем данные события console.log("Current balance:", e.detail.balance); }); const myAcc = new Account(100); // по нажатию на кнопку выполняем метод pay button.addEventListener("click", ()=>myAcc.pay(50)); function Account(money) { _money = money; this.pay=function(sum){ if(_money >= sum){ _money -= sum; // определяем объект события const event = new CustomEvent("payment", { detail:{ // передаем в CustomEvent данные о событии paymentSum: sum, balance: _money } }); document.dispatchEvent(event); // генерируем событие для всего документа } } }
В CustomEvent в качестве второго параметра передается конфигурационный объект, который имеет свойство detail. Это свойство в свою очередь представляет объект с произвольным набором свойств. В данном случае мы определяем в нем свойства paymentSum и balance и передаем этим свойствам интересующие нас значения:
const event = new CustomEvent("payment", { detail:{ paymentSum: sum, balance: _money } });
Далее передаем объект CustomEvent (как и Event) в dispatchEvent и тем самым генерируем событие:
document.dispatchEvent(event);
При обработке события мы можем получить переданные данные через свойство detail
:
document.addEventListener("payment", (e)=>{ console.log("Payment succeeded!"); console.log("Payment Sum:", e.detail.paymentSum); // получаем данные события console.log("Current balance:", e.detail.balance); });
Пример консольного вывода при первом нажатии кнопки:
Payment succeeded! Payment Sum: 50 Current balance: 50
Подобным образом можно определять и другие события. Например, определим еще одно событие на случай, если средств недостаточно для совершения платежа:
const button = document.getElementById("btn"); document.addEventListener("payment_success", (e)=>{ console.log("Payment succeeded!"); console.log("Payment Sum:", e.detail.paymentSum); console.log("Current balance:", e.detail.balance); }); document.addEventListener("payment_fail", (e)=>{ console.error("Payment failed"); console.error("Current balance:", e.detail.balance, "Requested Sum: ", e.detail.paymentSum); }); const myAcc = new Account(100); button.addEventListener("click", ()=>myAcc.pay(50)); function Account(money) { _money = money; this.pay=function(sum){ const data = { paymentSum: sum, balance: _money }; if(_money >= sum){ _money -= sum; const event = new CustomEvent("payment_success", { detail: data }); document.dispatchEvent(event); } else{ const event = new CustomEvent("payment_fail", { detail: data }); document.dispatchEvent(event); } } }
Теперь, если средст достаточно на счете генерируется событие "payment_success", а если недостаточно - то "payment_fail". И для каждого из этих событий определяем свой обработчик.
консольный вывод программы (при трех нажатиях на кнопку):
Payment succeeded!
Payment Sum: 50
Current balance: 100
Payment succeeded!
Payment Sum: 50
Current balance: 50
Payment failed
Current balance: 0 Requested Sum: 50