Браузерные события, часть 5 из 5. Генерация пользовательских событий
29.04.2022
Теги: DOM • Frontend • HTML • JavaScript • Web-разработка • Событие • Теория
Можно не только назначать обработчики, но и генерировать события из js-кода. Например, корневой элемент меню, реализованного с помощью javascript, может генерировать события, относящиеся к этому меню: open
(меню раскрыто), select
(выбран пункт меню) и т.п. А другой код может слушать эти события и узнавать, что происходит с меню.
Можно генерировать не только совершенно новые, придуманные нами события, но и встроенные, такие как click
, mousedown
и другие. Это бывает полезно для автоматического тестирования.
Конструктор Event
Встроенные классы для событий формируют иерархию аналогично классам для DOM-элементов. Её корнем является встроенный класс Event
. Событие встроенного класса Event
можно создать так:
let event = new Event(type[, options]);
Здесь
type
— тип события, строка, напримерclick
или же любой придуманный намиoptions
— объект с тремя необязательными свойствамиbubbles
— еслиtrue
, тогда событие всплываетcancelable
— еслиtrue
, тогда можно отменить действие по умолчаниюcomposed
— еслиtrue
, тогда событие будет всплывать наружу за пределы Shadow DOM
По умолчанию все три свойства установлены в false
: {bubbles:false, cancelable:false, composed:false}
.
Метод dispatchEvent
После того, как объект события создан, мы должны запустить его на элементе, вызвав метод elem.dispatchEvent(event)
. Затем обработчики отреагируют на него, как будто это обычное браузерное событие. Если при создании объекта события указан флаг bubbles
, то событие будет всплывать.
В примере ниже событие click
инициируется javascript-кодом так, как будто кликнули по кнопке:
<button id="elem" onclick="alert('Клик!');">Автоклик</button> <script> let event = new Event('click'); elem.dispatchEvent(event); </script>
event.isTrusted
принимает значение true
для событий, порождаемых реальными действиями пользователя, и false
— для генерируемых кодом.
Пример всплытия
Мы можем создать всплывающее событие с именем hello
и поймать его на document
. Всё, что нужно сделать — это установить флаг bubbles
в значение true
.
<h1 id="elem">Привет из кода!</h1> <script> // ловим событие на document document.addEventListener('hello', function(event) { alert('Привет от ' + event.target.tagName); }); // запуск события на элементе let event = new Event('hello', {bubbles: true}); elem.dispatchEvent(event); </script>
Обратите внимание:
- Мы должны использовать
addEventListener
для наших собственных событий, т.к.on{event}
-свойства существуют только для встроенных событий, то естьdocument.onhello
не сработает. - Мы обязаны передать флаг
bubbles:true
, иначе наше событие не будет всплывать.
Механизм всплытия идентичен как для встроенного события (click
), так и для пользовательского события (hello
). Также одинакова работа фаз всплытия и погружения.
MouseEvent, KeyboardEvent и другие
Для некоторых конкретных типов событий есть свои специфические конструкторы. Вот небольшой список конструкторов для различных событий пользовательского интерфейса, которые можно найти в спецификации UI Event
:
UIEvent
FocusEvent
MouseEvent
WheelEvent
KeyboardEvent
..........
Стоит использовать их вместо Event
, если мы хотим создавать такие события, например — MouseEvent("click")
. Специфический конструктор позволяет указать стандартные свойства для данного типа события.
// clientX/clientY для события мыши let event = new MouseEvent('click', { bubbles: true, cancelable: true, clientX: 100, clientY: 100 }); alert(event.clientX); // 100
Обратите внимание — этого нельзя было бы сделать с обычным конструктором Event
.
Впрочем, использование конкретного конструктора не является обязательным, можно обойтись Event
, а свойства записать в объект отдельно, после создания, вот так event.clientX=100
. Здесь это скорее вопрос удобства и желания следовать правилам.
Пользовательские события
Для генерации событий совершенно новых типов, таких как hello
, следует использовать конструктор CustomEvent
. Технически CustomEvent
абсолютно идентичен Event
за исключением одной небольшой детали. У второго аргумента-объекта есть дополнительное свойство detail
, в котором можно указывать информацию для передачи в событие.
<h1 id="elem">Привет для Васи!</h1> <script> elem.addEventListener('hello', function(event) { alert(event.detail.name); // Вася }); elem.dispatchEvent(new CustomEvent('hello', { detail: { name: 'Вася' } })); </script>
Свойство detail
может содержать любые данные. Надо сказать, что никто не мешает и в обычное Event
записать любые свойства. Но CustomEvent
предоставляет специальное поле detail
во избежание конфликтов с другими свойствами события.
Кроме того, класс события описывает, что это за событие, и если оно не браузерное, а пользовательское, то лучше использовать CustomEvent
, чтобы явно об этом сказать.
event.preventDefault()
Для пользовательского события «действия по умолчанию» нет, но код, который генерирует такое событие, может предусматривать какие-то свои действия после события. Вызов event.preventDefault()
является возможностью для обработчика события сообщить в сгенерировавший событие код, что эти действия надо отменить. Тогда вызов elem.dispatchEvent(event)
возвратит false
. И код, сгенерировавший событие, узнает, что продолжать не нужно.
<ul id="menu"> <li><a href="/html">HTML</a></li> <li><a href="/javascript">JavaScript</a></li> <li><a href="/css">CSS</a></li> </ul> <button id="hide">Hide</button> <script> // при клике на кнопке генерируем событие hide-menu на элементе button hide.onclick = function() { let event = new CustomEvent('hide-menu', { cancelable: true // без этого флага preventDefault не сработает }); if (!menu.dispatchEvent(event)) { alert('Действие отменено обработчиком'); } else { menu.hidden = true; } } menu.addEventListener('hide-menu', function(event) { if (confirm('Вызвать preventDefault?')) { event.preventDefault(); } }); </script>
- Браузерные события, часть 4 из 5. Действие браузера по умолчанию
- Браузерные события, часть 3 из 5. Делегирование
- Браузерные события, часть 2 из 5. Погружение и всплытие
- Браузерные события, часть 1 из 5. Основные сведения
- DOM, часть 4 из 4. Атрибуты и свойства
- DOM, часть 3 из 3. Поиск элементов
- DOM, часть 2 из 3. Изменение документа
Поиск: DOM • Frontend • HTML • JavaScript • Web-разработка • Событие • Теория