renovations
This commit is contained in:
parent
e1d099ae97
commit
53d9080aad
50 changed files with 653 additions and 471 deletions
|
@ -1,8 +1,10 @@
|
|||
# Генерация событий на элементах
|
||||
|
||||
Можно не только слушать браузерные события, но и генерировать их самому.
|
||||
Можно не только назначать обработчики на события, но и генерировать их самому.
|
||||
|
||||
Это нужно довольно редко, преимущественно -- для целей автоматического тестирования.
|
||||
Мы будем использовать это позже для реализации компонентной архитектуры, при которой элемент, представляющий собой, к примеру, меню, генерирует события, к этому меню относящиеся -- `select` (выбран пункт меню) или `open` (меню раскрыто), и другие.
|
||||
|
||||
Кроме того, события можно генерировать для целей автоматического тестирования.
|
||||
|
||||
[cut]
|
||||
|
||||
|
@ -10,7 +12,7 @@
|
|||
|
||||
Вначале рассмотрим современный способ генерации событий, по стандарту [DOM 4](http://www.w3.org/TR/dom/#introduction-to-dom-events). Он поддерживается всеми браузерами, кроме IE11-. А далее рассмотрим устаревшие варианты, поддерживаемые IE.
|
||||
|
||||
**Объект события создаётся при помощи конструктора [Event](http://www.w3.org/TR/dom/#event).**
|
||||
Объект события в нём создаётся при помощи встроенного конструктора [Event](http://www.w3.org/TR/dom/#event).
|
||||
|
||||
Синтаксис:
|
||||
|
||||
|
@ -23,16 +25,16 @@ var event = new Event(тип события[, флаги]);
|
|||
<li>*Тип события* -- может быть как своим, так и встроенным, к примеру `"click"`.</li>
|
||||
<li>*Флаги* -- объект вида `{ bubbles: true/false, cancelable: true/false }`, где свойство `bubbles` указывает, всплывает ли событие, а `cancelable` -- можно ли отменить действие по умолчанию.
|
||||
|
||||
Не обязателен, по умолчанию `{bubbles: false, cancelable: false}`.</li>
|
||||
Флаги по умолчанию: `{bubbles: false, cancelable: false}`.</li>
|
||||
</ul>
|
||||
|
||||
### Метод dispatchEvent
|
||||
## Метод dispatchEvent
|
||||
|
||||
Затем, чтобы инициировать событие, запускается `elem.dispatchEvent(event)`.
|
||||
|
||||
Событие обрабатывается "как обычно", передаётся обработчикам, всплывает... Этот метод возвращает `false`, если событие было отменено при помощи `preventDefault()`. Конечно, отмена возможна лишь если событие изначально было создано с флагом `cancelable`.
|
||||
При этом событие срабатывает наравне с браузерными, то есть обычные браузерные обработчики на него отреагируют. Если при создании указан флаг `bubbles`, то оно будет всплывать.
|
||||
|
||||
При просмотре примера ниже кнопка будет нажата скриптом:
|
||||
При просмотре примера ниже кнопка обработчик `onclick` на кнопке сработает сам по себе, событие генерируется скриптом:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
|
@ -44,6 +46,50 @@ var event = new Event(тип события[, флаги]);
|
|||
</script>
|
||||
```
|
||||
|
||||
## Отмена действия по умолчанию
|
||||
|
||||
На сгенерированном событии, как и на встроенном браузерном, обработчик может вызвать метод `event.preventDefault()`. Тогда `dispatchEvent` возвратит `false`.
|
||||
|
||||
Остановимся здесь подробнее. Обычно такой вызов предотвращает действие браузера. В случае, если событие придумано нами -- никакого действия браузера, конечно, нет, но код, который генерирует событие, может быть заинтересован узнать, что его "отменили" и не продолжать свои действия.
|
||||
|
||||
Иначе говоря, `event.preventDefault()` является возможностью для обработчика сообщить в сгенерировавший событие код, что некие действия продолжать не надо.
|
||||
|
||||
В примере ниже функция `hide()` генерирует событие `hide` на элементе `#rabbit`, уведомляя всех интересующихся, что кролик собирается спрятаться.
|
||||
|
||||
И, если никакой обработчик не отменит действие по умолчанию, то кролик действительно исчезнет:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<pre id="rabbit">
|
||||
|\ /|
|
||||
\|_|/
|
||||
/. .\
|
||||
=\_Y_/=
|
||||
{>o<}
|
||||
</pre>
|
||||
|
||||
|
||||
<script>
|
||||
// прячемся через 2 секунды
|
||||
setTimeout(hide, 2000);
|
||||
|
||||
function hide() {
|
||||
var event = new Event("hide", {cancelable: true});
|
||||
if (!rabbit.dispatchEvent(event)) {
|
||||
alert('действие отменено');
|
||||
} else {
|
||||
rabbit.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
rabbit.addEventListener('hide', function(event) {
|
||||
if (confirm("Вызвать preventDefault?")) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
[smart header="Как отличить реальное нажатие от скриптового?"]
|
||||
В целях безопасности иногда хорошо бы знать -- инициировано ли действие посетителем или это кликнул скрипт.
|
||||
|
||||
|
@ -52,7 +98,9 @@ var event = new Event(тип события[, флаги]);
|
|||
Оно на момент написания статьи поддерживается IE и Firefox и равно `true`, если посетитель кликнул сам, и всегда `false` -- если событие инициировал скрипт.
|
||||
[/smart]
|
||||
|
||||
Браузер автоматически ставит следующие свойства объекта `event`:
|
||||
## Другие свойства событий
|
||||
|
||||
При создании события браузер автоматически ставит следующие свойства:
|
||||
|
||||
<ul>
|
||||
<li>`isTrusted: false` -- означает, что событие сгенерировано скриптом, это свойство изменить невозможно.</li>
|
||||
|
@ -64,16 +112,16 @@ var event = new Event(тип события[, флаги]);
|
|||
Другие свойства события, если они нужны, например координаты для события мыши -- можно присвоить в объект события позже, например:
|
||||
|
||||
```js
|
||||
var event = new Event("click");
|
||||
var event = new Event("click", {bubbles: true, cancelable: false});
|
||||
event.clientX = 100;
|
||||
event.clientY = 100;
|
||||
```
|
||||
|
||||
### Пример с hello
|
||||
## Пример со всплытием
|
||||
|
||||
Можно генерировать события с любыми названиями.
|
||||
Сгенерируем совершенно новое событие `"hello"` и поймаем его на `document`.
|
||||
|
||||
Для примера сгенерируем совершенно новое событие `"hello"`:
|
||||
Всё, что для этого нужно -- это флаг `bubbles`:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
|
@ -99,11 +147,11 @@ event.clientY = 100;
|
|||
<li>Чтобы событие всплывало и его можно было отменить, указан второй аргумент `new Event`.</li>
|
||||
</ol>
|
||||
|
||||
Никакой разницы между встроенными событиями (`click`) и своими (`hello`) здесь нет, они создаются и работают совершенно одинаково.
|
||||
Никакой разницы между встроенными событиями (`click`) и своими (`hello`) здесь нет, их можно сгенерировать и запустить совершенно одинаково.
|
||||
|
||||
## Конструкторы MouseEvent, KeyboardEvent и другие
|
||||
|
||||
Для конкретных типов событий есть свои конструкторы.
|
||||
Для некоторых конкретных типов событий есть свои, специфические, конструкторы.
|
||||
|
||||
Вот список конструкторов для различных событий интерфейса которые можно найти в спецификации [UI Event](http://www.w3.org/TR/uievents/):
|
||||
<ul>
|
||||
|
@ -117,9 +165,9 @@ event.clientY = 100;
|
|||
|
||||
Вместо `new Event("click")` можно вызвать `new MouseEvent("click")`.
|
||||
|
||||
**Конкретный конструктор позволяет указать стандартные свойства для данного типа события.**
|
||||
**Специфический конструктор позволяет указать стандартные свойства для данного типа события.**
|
||||
|
||||
Например:
|
||||
Например, `clientX/clientY` для события мыши:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -135,7 +183,7 @@ alert(e.clientX); // 100
|
|||
*/!*
|
||||
```
|
||||
|
||||
Сравните это с обычным `Event`:
|
||||
Это нельзя было бы сделать с обычным конструктором `Event`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
@ -147,21 +195,19 @@ var e = new Event("click", {
|
|||
});
|
||||
|
||||
*!*
|
||||
alert(e.clientX); // undefined
|
||||
alert(e.clientX); // undefined, свойство не присвоено!
|
||||
*/!*
|
||||
```
|
||||
|
||||
...То есть, "мышиные" свойства можно сразу же в конструкторе указать только если это `MouseEvent`, а `Event` их игнорирует.
|
||||
Обычный конструктор `Event` не знает про "мышиные" свойства, поэтому их игнорирует.
|
||||
|
||||
**Использование конкретного конструктора не является обязательным, можно обойтись `Event`.**
|
||||
|
||||
Свойства можно присвоить и явно, после конструктора. Здесь это скорее вопрос удобства и желания следовать правилам. События, которые генерирует браузер, всегда имеют правильный тип.
|
||||
Впрочем, использование конкретного конструктора не является обязательным, можно обойтись `Event`, а свойства записать в объект отдельно, после конструктора. Здесь это скорее вопрос удобства и желания следовать правилам. События, которые генерирует браузер, всегда имеют правильный тип.
|
||||
|
||||
Полный список свойств по типам событий вы найдёте в спецификации, например для `MouseEvent`: [MouseEvent Constructor](http://www.w3.org/TR/uievents/#constructor-mouseevent).
|
||||
|
||||
## Свои события
|
||||
|
||||
Для генерации встроенных событий существуют описанные выше конструкторы, а для генерации своих, нестандартных, событий существует конструктор [CustomEvent](http://www.w3.org/TR/dom/#customevent).
|
||||
Для генерации своих, нестандартных, событий, хоть и можно использовать конструктор `Event`, но существует и специфический конструктор [CustomEvent](http://www.w3.org/TR/dom/#customevent).
|
||||
|
||||
Технически, он абсолютно идентичен `Event`, кроме небольшой детали: у второго аргумента-объекта есть дополнительное свойство `detail`, в котором можно указывать информацию для передачи в событие.
|
||||
|
||||
|
@ -190,9 +236,13 @@ alert(e.clientX); // undefined
|
|||
|
||||
## Старое API для IE9+
|
||||
|
||||
В предыдущем стандарте [DOM 3 Events](http://www.w3.org/TR/DOM-Level-3-Events) была предусмотрена [иерархия событий](http://www.w3.org/TR/DOM-Level-3-Events/#event-interfaces), с различными методами инициализации.
|
||||
Способ генерации событий, описанный выше, не поддерживается в IE11-, там нужен другой, более старый способ, описанный в стандарте [DOM 3 Events](http://www.w3.org/TR/DOM-Level-3-Events).
|
||||
|
||||
Она поддерживается как современными браузерами, так и IE9+. Для генерации событий используется немного другой синтаксис, но по возможностям -- всё то же самое, что и в современном стандарте.
|
||||
В нём была предусмотрена [иерархия событий](http://www.w3.org/TR/DOM-Level-3-Events/#event-interfaces), с различными методами инициализации.
|
||||
|
||||
Она поддерживается как современными браузерами, так и IE9+. Там используется немного другой синтаксис, но по возможностям -- всё то же самое, что и в современном стандарте.
|
||||
|
||||
Можно использовать этот немного устаревший способ, если нужно поддерживать IE9+. Далее мы на его основе создадим полифилл.
|
||||
|
||||
Объект события создаётся вызовом `document.createEvent`:
|
||||
|
||||
|
@ -206,7 +256,7 @@ var event = document.createEvent(eventInterface);
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
**На практике можно всегда использовать самый общий интерфейс: `document.createEvent("Event")`.**
|
||||
На практике можно всегда использовать самый общий интерфейс: `document.createEvent("Event")`.
|
||||
|
||||
Далее событие нужно инициализовать:
|
||||
|
||||
|
@ -306,9 +356,37 @@ void initMouseEvent (
|
|||
</script>
|
||||
```
|
||||
|
||||
Браузер, по стандарту, может сгенерировать отсутствующие свойства самостоятельно, например `pageX`, но это нужно проверять в конкретных случаях, обычно это не работает или работает некорректно.
|
||||
Браузер, по стандарту, может сгенерировать отсутствующие свойства самостоятельно, например `pageX`, но это нужно проверять в конкретных случаях, иногда это не работает или работает некорректно, так что лучше указать все.
|
||||
[/smart]
|
||||
|
||||
## Полифилл CustomEvent
|
||||
|
||||
Для поддержки `CustomEvent` в IE9+ можно сделать небольшой полифилл:
|
||||
|
||||
```js
|
||||
try {
|
||||
new CustomEvent("IE has CustomEvent, but doesn't support constructor");
|
||||
} catch (e) {
|
||||
|
||||
window.CustomEvent = function(event, params) {
|
||||
var evt;
|
||||
params = params || {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
detail: undefined
|
||||
};
|
||||
evt = document.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
||||
return evt;
|
||||
};
|
||||
|
||||
CustomEvent.prototype = Object.create(window.Event.prototype);
|
||||
}
|
||||
```
|
||||
|
||||
Здесь мы сначала проверяем -- в IE9-11 есть `CustomEvent`, но его нельзя создать через `new`, будет ошибка. В этом случае заменяем браузерную реализацию на свою, совместимую.
|
||||
|
||||
|
||||
## Антистандарт: IE8-
|
||||
|
||||
В совсем старом IE были "свои" методы `document.createEventObject()` и `elem.fireEvent()`.
|
||||
|
@ -340,39 +418,20 @@ void initMouseEvent (
|
|||
|
||||
Параметры `bubbles` и `cancelable` настраивать нельзя, браузер использует стандартные для данного типа событий.
|
||||
|
||||
## Кросс-браузерный пример
|
||||
Существуют полифиллы для генерации произвольных событий и для IE8-, но они, по сути, полностью подменяют встроенную систему обработки событий браузером. И кода это требует тоже достаточно много.
|
||||
|
||||
Для поддержки IE9+ достаточно использовать методы `document.createEvent` и `event.initEvent`, как показано выше, и всё будет хорошо.
|
||||
Альтернатива -- фреймворк, например jQuery, который также реализует свою мощную систему работы с событиями, доступную через методы jQuery.
|
||||
|
||||
Если же нужен IE8, то подойдёт такой код:
|
||||
|
||||
```js
|
||||
function trigger(elem, type){
|
||||
if (document.createEvent) {
|
||||
var event = document.createEvent('Event') :
|
||||
event.initEvent(type);
|
||||
return elem.dispatchEvent(event);
|
||||
}
|
||||
|
||||
var event = document.createEventObject();
|
||||
return elem.fireEvent("on"+type, event);
|
||||
}
|
||||
|
||||
// использование:
|
||||
trigger(elem, "click");
|
||||
```
|
||||
|
||||
Конечно, надо иметь в виду, что в IE8 события можно использовать только встроенные, а `bubbles` и `cancelable` поставить нельзя.
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Все браузеры, кроме IE, позволяют генерировать любые события, следуя стандарту DOM4.</li>
|
||||
<li>IE9+ тоже справляется, если использовать вызовы более старого стандарта, и имеет в итоге тот же функционал.</li>
|
||||
<li>Все браузеры, кроме IE9-11, позволяют генерировать любые события, следуя стандарту DOM4.</li>
|
||||
<li>В IE9+ поддерживается более старый стандарт, можно легко сделать полифилл, например для `CustomEvent` он рассмотрен в этой главе.</li>
|
||||
<li>IE8- может генерировать только встроенные события.</li>
|
||||
</ul>
|
||||
|
||||
**Несмотря на техническую возможность генерировать браузерные события -- пользоваться ей стоит с большой осторожностью.**
|
||||
Несмотря на техническую возможность генерировать встроенные браузерные события типа `click` или `keydown` -- пользоваться ей стоит с большой осторожностью.
|
||||
|
||||
В 98% случаев, когда разработчик начинающего или среднего уровня хочет сгенерировать *встроенное* событие -- это вызвано "кривой" архитектурой кода, и взаимодействие нужно на уровне выше.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue