up
This commit is contained in:
parent
cf9969f7ab
commit
497c9fd2d3
14 changed files with 268 additions and 596 deletions
|
@ -1,16 +1,16 @@
|
|||
Дело в том, что обработчик из атрибута `onclick` делается браузером как функция с заданным телом.
|
||||
When the browser reads the `on*` attribute like `onclick`, it creates the handler from its content.
|
||||
|
||||
То есть, в данном случае он будет таким:
|
||||
For `onclick="handler()"` the function will be:
|
||||
|
||||
```js
|
||||
function(event) {
|
||||
handler() // тело взято из атрибута onclick
|
||||
handler() // the content of onclick
|
||||
}
|
||||
```
|
||||
|
||||
При этом возвращаемое `handler` значение никак не используется и не влияет на результат.
|
||||
Now we can see that the value returned by `handler()` is not used and does not affect the result.
|
||||
|
||||
Рабочий вариант:
|
||||
The fix is simple:
|
||||
|
||||
```html run
|
||||
<script>
|
||||
|
@ -23,7 +23,7 @@ function(event) {
|
|||
<a href="http://w3.org" onclick="*!*return handler()*/!*">w3.org</a>
|
||||
```
|
||||
|
||||
Также можно использовать объект события для вызова `event.preventDefault()`, например:
|
||||
Also we can use `event.preventDefault()`, like this:
|
||||
|
||||
```html run
|
||||
<script>
|
||||
|
@ -37,4 +37,3 @@ function(event) {
|
|||
|
||||
<a href="http://w3.org" onclick="*!*handler(event)*/!*">w3.org</a>
|
||||
```
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ importance: 3
|
|||
|
||||
---
|
||||
|
||||
# Почему не работает return false?
|
||||
# Why "return false" doesn't work?
|
||||
|
||||
Почему в этом документе `return false` не работает?
|
||||
Why in the code below `return false` doesn't work at all?
|
||||
|
||||
```html autorun run
|
||||
<script>
|
||||
|
@ -14,9 +14,9 @@ importance: 3
|
|||
}
|
||||
</script>
|
||||
|
||||
<a href="http://w3.org" onclick="handler()">w3.org</a>
|
||||
<a href="http://w3.org" onclick="handler()">the browser will go to w3.org</a>
|
||||
```
|
||||
|
||||
По замыслу, переход на `w3.org` при клике должен отменяться. Однако, на самом деле он происходит.
|
||||
The browser follows the URL on click, but we don't want it.
|
||||
|
||||
В чём дело и как поправить?
|
||||
How to fix?
|
||||
|
|
|
@ -1,28 +1,5 @@
|
|||
Это -- классическая задача на тему делегирования.
|
||||
That's a great use of the event delegation pattern.
|
||||
|
||||
В реальной жизни, мы можем перехватить событие и создать AJAX-запрос к серверу, который сохранит информацию о том, по какой ссылке ушел посетитель.
|
||||
|
||||
Мы перехватываем событие на `contents` и поднимаемся до `parentNode` пока не получим `A` или не упремся в контейнер.
|
||||
|
||||
```js
|
||||
contents.onclick = function(evt) {
|
||||
var target = evt.target;
|
||||
|
||||
function handleLink(href) {
|
||||
var isLeaving = confirm('Уйти на ' + href + '?');
|
||||
if (!isLeaving) return false;
|
||||
}
|
||||
|
||||
while (target != this) {
|
||||
if (target.nodeName == 'A') {
|
||||
*!*
|
||||
return handleLink(target.getAttribute('href')); // (*)
|
||||
*/!*
|
||||
}
|
||||
target = target.parentNode;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
В строке `(*)` используется атрибут, а не свойство `href`, чтобы показать в `confirm` именно то, что написано в HTML-атрибуте, так как свойство может отличаться, оно обязано содержать полный валидный адрес.
|
||||
In real life instead of asking we can send a "logging" request to the server that saves the information about where the visitor left. Or we can load the content and show it right in the page (if allowable).
|
||||
|
||||
All we need is to catch the `contents.onclick` and use `confirm` to ask the user. A good idea would be to use `link.getAttribute('href')` instead of `link.href` for the URL. See the solution for details.
|
||||
|
|
|
@ -16,28 +16,25 @@
|
|||
<fieldset id="contents">
|
||||
<legend>#contents</legend>
|
||||
<p>
|
||||
Как насчет почитать <a href="http://wikipedia.org">Википедию</a>, или посетить <a href="http://w3.org"><i>W3.org</i></a> и узнать про современные стандарты?
|
||||
How about to read <a href="http://wikipedia.org">Wikipedia</a> or visit <a href="http://w3.org"><i>W3.org</i></a> and learn about modern standards?
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<script>
|
||||
document.getElementById('contents').onclick = function(event) {
|
||||
contents.onclick = function(event) {
|
||||
|
||||
function handleLink(href) {
|
||||
var isLeaving = confirm('Уйти на ' + href + '?');
|
||||
let isLeaving = confirm(`Leave for ${href}?`);
|
||||
if (!isLeaving) return false;
|
||||
}
|
||||
|
||||
var target = event.target;
|
||||
let target = event.target.closest('a');
|
||||
|
||||
while (target != this) {
|
||||
if (target.nodeName == 'A') {
|
||||
return handleLink(target.getAttribute('href'));
|
||||
}
|
||||
target = target.parentNode;
|
||||
if (target && contents.contains(target)) {
|
||||
return handleLink(target.getAttribute('href'));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -16,10 +16,9 @@
|
|||
<fieldset id="contents">
|
||||
<legend>#contents</legend>
|
||||
<p>
|
||||
Как насчет почитать <a href="http://wikipedia.org">Википедию</a>, или посетить <a href="http://w3.org"><i>W3.org</i></a> и узнать про современные стандарты?
|
||||
How about to read <a href="http://wikipedia.org">Wikipedia</a> or visit <a href="http://w3.org"><i>W3.org</i></a> and learn about modern standards?
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -2,16 +2,15 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Поймайте переход по ссылке
|
||||
# Catch links in the element
|
||||
|
||||
Сделайте так, чтобы при клике на ссылки внутри элемента `#contents` пользователю выводился вопрос о том, действительно ли он хочет покинуть страницу и если он не хочет, то прерывать переход по ссылке.
|
||||
Make all links inside the element with `id="contents"` ask the user if he really wants to leave. And if he doesn't then don't follow.
|
||||
|
||||
Так это должно работать:
|
||||
Like this:
|
||||
|
||||
[iframe height=100 border=1 src="solution"]
|
||||
|
||||
Детали:
|
||||
|
||||
- Содержимое `#contents` может быть загружено динамически и присвоено при помощи `innerHTML`. Так что найти все ссылки и поставить на них обработчики нельзя. Используйте делегирование.
|
||||
- Содержимое может содержать вложенные теги, *в том числе внутри ссылок*, например, `<a href=".."><i>...</i></a>`.
|
||||
Details:
|
||||
|
||||
- HTML inside the element may be loaded or regenerated dynamically at any time, so we can't find all links and put handlers on them. Use the event delegation.
|
||||
- The content may have nested tags. Inside links too, like `<a href=".."><i>...</i></a>`.
|
||||
|
|
|
@ -1,54 +1 @@
|
|||
Решение состоит в том, чтобы добавить обработчик на контейнер `#thumbs` и отслеживать клики на ссылках.
|
||||
|
||||
Когда происходит событие, обработчик должен изменять `src` `#largeImg` на `href` ссылки и заменять `alt` на ее `title`.
|
||||
|
||||
Код решения:
|
||||
|
||||
```js
|
||||
var largeImg = document.getElementById('largeImg');
|
||||
|
||||
document.getElementById('thumbs').onclick = function(e) {
|
||||
var target = e.target;
|
||||
|
||||
while (target != this) {
|
||||
|
||||
if (target.nodeName == 'A') {
|
||||
showThumbnail(target.href, target.title);
|
||||
return false;
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function showThumbnail(href, title) {
|
||||
largeImg.src = href;
|
||||
largeImg.alt = title;
|
||||
}
|
||||
```
|
||||
|
||||
**Предзагрузка картинок**
|
||||
|
||||
Для того, чтобы картинка загрузилась, достаточно создать новый элемент `IMG` и указать ему `src`, вот так:
|
||||
|
||||
```js
|
||||
var imgs = thumbs.getElementsByTagName('img');
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var url = imgs[i].parentNode.href;
|
||||
|
||||
*!*
|
||||
var img = document.createElement('img');
|
||||
img.src = url;
|
||||
*/!*
|
||||
}
|
||||
```
|
||||
|
||||
Как только элемент создан и ему назначен `src`, браузер сам начинает скачивать файл картинки.
|
||||
|
||||
При правильных настройках сервера как-то использовать этот элемент не обязательно -- картинка уже закеширована.
|
||||
|
||||
**Семантичная верстка**
|
||||
|
||||
Для списка картинок используется `DIV`. С точки зрения семантики более верный вариант -- список `UL/LI`.
|
||||
|
||||
The solution is to assign the handler to the container and track clicks. If a click is on the `<a>` link, then change `src` of `#largeImg` to the `href` of the thumbnail.
|
||||
|
|
|
@ -2,69 +2,48 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Галерея</title>
|
||||
<title>Gallery</title>
|
||||
<link rel="stylesheet" href="gallery.css">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p><img id="largeImg" src="https://js.cx/gallery/img1-lg.jpg" alt="Large image"></p>
|
||||
<p><img id="largeImg" src="https://en.js.cx/gallery/img1-lg.jpg" alt="Large image"></p>
|
||||
|
||||
<ul id="thumbs">
|
||||
<!-- При наведении на изображение показывается встроенная подсказка браузера (title) -->
|
||||
<!-- the browser shows a small built-in tooltip on hover with the text from "title" attribute -->
|
||||
<li>
|
||||
<a href="https://js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="https://js.cx/gallery/img2-thumb.jpg"></a>
|
||||
<a href="https://en.js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="https://en.js.cx/gallery/img2-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="https://js.cx/gallery/img3-thumb.jpg"></a>
|
||||
<a href="https://en.js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="https://en.js.cx/gallery/img3-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="https://js.cx/gallery/img4-thumb.jpg"></a>
|
||||
<a href="https://en.js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="https://en.js.cx/gallery/img4-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="https://js.cx/gallery/img5-thumb.jpg"></a>
|
||||
<a href="https://en.js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="https://en.js.cx/gallery/img5-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="https://js.cx/gallery/img6-thumb.jpg"></a>
|
||||
<a href="https://en.js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="https://en.js.cx/gallery/img6-thumb.jpg"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
var largeImg = document.getElementById('largeImg');
|
||||
|
||||
var thumbs = document.getElementById('thumbs');
|
||||
|
||||
thumbs.onclick = function(e) {
|
||||
var target = e.target;
|
||||
|
||||
while (target != this) {
|
||||
|
||||
if (target.nodeName == 'A') {
|
||||
showThumbnail(target.href, target.title);
|
||||
return false;
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
}
|
||||
thumbs.onclick = function(event) {
|
||||
let thumbnail = event.target.closest('a');
|
||||
|
||||
if (!thumbnail) return;
|
||||
showThumbnail(target.href, target.title);
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function showThumbnail(href, title) {
|
||||
largeImg.src = href;
|
||||
largeImg.alt = title;
|
||||
}
|
||||
|
||||
|
||||
/* предзагрузка */
|
||||
var imgs = thumbs.getElementsByTagName('img');
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
var url = imgs[i].parentNode.href;
|
||||
var img = document.createElement('img');
|
||||
img.src = url;
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -2,24 +2,33 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Галерея</title>
|
||||
<title>Gallery</title>
|
||||
<link rel="stylesheet" href="gallery.css">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p><img id="largeImg" src="https://js.cx/gallery/img1-lg.jpg" alt="Large image"></p>
|
||||
<p><img id="largeImg" src="https://en.js.cx/gallery/img1-lg.jpg" alt="Large image"></p>
|
||||
|
||||
<div id="thumbs">
|
||||
<!-- При наведении на изображение показывается встроенная подсказка браузера (title) -->
|
||||
<a href="https://js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="https://js.cx/gallery/img2-thumb.jpg"></a>
|
||||
<a href="https://js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="https://js.cx/gallery/img3-thumb.jpg"></a>
|
||||
<a href="https://js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="https://js.cx/gallery/img4-thumb.jpg"></a>
|
||||
<a href="https://js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="https://js.cx/gallery/img5-thumb.jpg"></a>
|
||||
<a href="https://js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="https://js.cx/gallery/img6-thumb.jpg"></a>
|
||||
</div>
|
||||
<ul id="thumbs">
|
||||
<!-- the browser shows a small built-in tooltip on hover with the text from "title" attribute -->
|
||||
<li>
|
||||
<a href="https://en.js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="https://en.js.cx/gallery/img2-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://en.js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="https://en.js.cx/gallery/img3-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://en.js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="https://en.js.cx/gallery/img4-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://en.js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="https://en.js.cx/gallery/img5-thumb.jpg"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://en.js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="https://en.js.cx/gallery/img6-thumb.jpg"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -2,19 +2,12 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Галерея изображений
|
||||
# Image gallery
|
||||
|
||||
Создайте галерею изображений, в которой основное изображение изменяется при клике на уменьшенный вариант.
|
||||
Create an image gallery where the main image changes by the click on a thumbnail.
|
||||
|
||||
Результат должен выглядеть так:
|
||||
Like this:
|
||||
|
||||
[iframe src="solution" height=600]
|
||||
|
||||
Для обработки событий используйте делегирование, т.е. не более одного обработчика.
|
||||
|
||||
P.S. Обратите внимание -- клик может быть как на маленьком изображении `IMG`, так и на `A` вне него. При этом `event.target` будет, соответственно, либо `IMG`, либо `A`.
|
||||
|
||||
Дополнительно:
|
||||
|
||||
- Если получится -- сделайте предзагрузку больших изображений, чтобы при клике они появлялись сразу.
|
||||
- Всё ли в порядке с семантической вёрсткой в HTML исходного документа? Если нет -- поправьте, чтобы было, как нужно.
|
||||
P.S. Use event delegation.
|
||||
|
|
|
@ -1,145 +1,116 @@
|
|||
# Действия браузера по умолчанию
|
||||
# Browser actions
|
||||
|
||||
Многие события автоматически влекут за собой действие браузера.
|
||||
Many events automatically lead to browser actions.
|
||||
|
||||
Например:
|
||||
For instance:
|
||||
|
||||
- Клик по ссылке инициирует переход на новый URL.
|
||||
- Нажатие на кнопку "отправить" в форме -- отсылку ее на сервер.
|
||||
- Двойной клик на тексте -- инициирует его выделение.
|
||||
- A click on a link -- initiates going to its URL.
|
||||
- A click on submit button inside a form -- initiates its submission to the server.
|
||||
- Pressing a mouse button over a text and moving it -- selects the text.
|
||||
|
||||
Если мы обрабатываем событие в JavaScript, то зачастую такое действие браузера нам не нужно. К счастью, его можно отменить.
|
||||
If we handle an event in JavaScript, often we don't want browser actions. Fortunately, it can be prevented.
|
||||
|
||||
[cut]
|
||||
|
||||
## Отмена действия браузера
|
||||
## Preventing browser actions
|
||||
|
||||
Есть два способа отменить действие браузера:
|
||||
There are two ways to tell the browser we don't want it to act:
|
||||
|
||||
- **Основной способ -- это воспользоваться объектом события. Для отмены действия браузера существует стандартный метод `event.preventDefault()`.**
|
||||
- Если же обработчик назначен через `onсобытие` (не через `addEventListener`), то можно просто вернуть `false` из обработчика.
|
||||
- The main way is to use the `event` object. There's a method `event.preventDefault()`.
|
||||
- If the handler is assigned using `on<event>` (not by `addEventListener`), then we can just return `false` from it.
|
||||
|
||||
В следующем примере при клике по ссылке переход не произойдет:
|
||||
In the example below there a click to links don't lead to URL change:
|
||||
|
||||
```html autorun height=60 no-beautify
|
||||
<a href="/" onclick="return false">Нажми здесь</a>
|
||||
или
|
||||
<a href="/" onclick="event.preventDefault()">здесь</a>
|
||||
<a href="/" onclick="return false">Click here</a>
|
||||
or
|
||||
<a href="/" onclick="event.preventDefault()">here</a>
|
||||
```
|
||||
|
||||
```warn header="Возвращать `true` не нужно"
|
||||
Обычно значение, которое возвращает обработчик события, игнорируется.
|
||||
```warn header="Not necessary to return `true`"
|
||||
The value returned by an event handler is usually ignored.
|
||||
|
||||
Единственное исключение -- это `return false` из обработчика, назначенного через `onсобытие`.
|
||||
The only exception -- is `return false` from a handler assigned using `on<event>`.
|
||||
|
||||
Иногда в коде начинающих разработчиков можно увидеть `return` других значений. Но они не нужны и никак не обрабатываются.
|
||||
In all other cases, the return is not needed and it's not processed anyhow.
|
||||
```
|
||||
|
||||
### Пример: меню
|
||||
### Example: the menu
|
||||
|
||||
Рассмотрим задачу, когда нужно создать меню для сайта, например такое:
|
||||
Consider a site menu, like this:
|
||||
|
||||
```html
|
||||
<ul id="menu" class="menu">
|
||||
<li><a href="/php">PHP</a></li>
|
||||
<li><a href="/html">HTML</a></li>
|
||||
<li><a href="/javascript">JavaScript</a></li>
|
||||
<li><a href="/flash">Flash</a></li>
|
||||
<li><a href="/css">CSS</a></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Данный пример при помощи CSS может выводиться так:
|
||||
Here's how it looks with some CSS:
|
||||
|
||||
[iframe height=70 src="menu" link edit]
|
||||
|
||||
HTML-разметка сделана так, что все элементы меню являются не кнопками, а ссылками, то есть тегами `<a>`.
|
||||
Menu items are links `<a>`, not buttons. There are several benefits, for instance:
|
||||
|
||||
Это потому, что некоторые посетители очень любят сочетание "правый клик - открыть в новом окне". Да, мы можем использовать и `<button>` и `<span>`, но если правый клик не работает -- это их огорчает. Кроме того, если на сайт зайдёт поисковик, то по ссылке из `<a href="...">` он перейдёт, а выполнить сложный JavaScript и получить результат -- вряд ли захочет.
|
||||
- Many people like to use "right click" -- "open in a new window". If we use `<button>` or `<span>`, that doesn't work.
|
||||
- Search engines follow `<a href="...">` links while indexing.
|
||||
|
||||
Поэтому в разметке мы используем именно `<a>`, но обычно клик будет обрабатываться полностью в JavaScript, а стандартное действие браузера (переход по ссылке) -- отменяться.
|
||||
So we use `<a>` in the markup. But normally we intend to handle clicks in JavaScript. So we should prevent the default browser action.
|
||||
|
||||
Например, вот так:
|
||||
Like here:
|
||||
|
||||
```js
|
||||
menu.onclick = function(event) {
|
||||
if (event.target.nodeName != 'A') return;
|
||||
|
||||
var href = event.target.getAttribute('href');
|
||||
alert( href ); // может быть подгрузка с сервера, генерация интерфейса и т.п.
|
||||
let href = event.target.getAttribute('href');
|
||||
alert( href ); // ...can be loading from the server, UI generation etc
|
||||
|
||||
*!*
|
||||
return false; // отменить переход по url
|
||||
return false; // prevent browser action (don't go to the URL)
|
||||
*/!*
|
||||
};
|
||||
```
|
||||
|
||||
В конце `return false`, иначе браузер перейдёт по адресу из `href`.
|
||||
If we omit `return false`, then after our code executes the browser will do its "default action" -- following to the URL in `href`.
|
||||
|
||||
Так как мы применили делегирование, то меню может увеличиваться, можно добавить вложенные списки `ul/li`, стилизовать их при помощи CSS -- обработчик не потребует изменений.
|
||||
By the way, using event delegation here makes our menu flexible. We can add nested lists and style them using CSS to "slide down".
|
||||
|
||||
## Другие действия браузера
|
||||
## Other browser actions
|
||||
|
||||
Действий браузера по умолчанию достаточно много.
|
||||
There are many default browser actions.
|
||||
|
||||
Вот некоторые примеры событий, которые вызывают действие браузера:
|
||||
Here are more events that cause browser actions:
|
||||
|
||||
- `mousedown` -- нажатие кнопкой мыши в то время как курсор находится на тексте начинает его выделение.
|
||||
- `click` на `<input type="checkbox">` -- ставит или убирает галочку.
|
||||
- `submit` -- при нажатии на `<input type="submit">` в форме данные отправляются на сервер.
|
||||
- `wheel` -- движение колёсика мыши инициирует прокрутку.
|
||||
- `keydown` -- при нажатии клавиши в поле ввода появляется символ.
|
||||
- `contextmenu` -- при правом клике показывается контекстное меню браузера.
|
||||
- `mousedown` -- starts the selection (move the mouse to select).
|
||||
- `click` on `<input type="checkbox">` -- checks/unchecks the `input`.
|
||||
- `submit` -- clicking an `<input type="submit">` or hitting `key:Enter` inside a form field causes this event to happen, and the browser submits the form after it.
|
||||
- `wheel` -- rolling a mouse wheel event has scrolling as the default action.
|
||||
- `keydown` -- pressing a key may lead to adding a character into a field, or other actions.
|
||||
- `contextmenu` -- the event happens on a right-click, the action is to show the browser context menu.
|
||||
- ...
|
||||
|
||||
Все эти действия можно отменить, если мы хотим обработать событие исключительно при помощи JavaScript.
|
||||
All the default actions can be prevented if we want to handle the event exclusively by JavaScript.
|
||||
|
||||
````warn header="События могут быть связаны между собой"
|
||||
Некоторые события естественным образом вытекают друг из друга.
|
||||
````warn header="Events may be related"
|
||||
Certain events flow one into another.
|
||||
|
||||
Например, нажатие мышкой `mousedown` на поле ввода `<input>` приводит к фокусировке внутрь него. Если отменить действие `mousedown`, то и фокуса не будет.
|
||||
For instance, `mousedown` on an `<input>` field leads to focusing in it, and the `focus` event. If we prevent the `mousedown` event, there's no focus.
|
||||
|
||||
Попробуйте нажать мышкой на первый `<input>` -- произойдёт событие `onfocus`. Это обычная ситуация.
|
||||
Try to click on the first `<input>` below -- the `focus` event happens. That's normal.
|
||||
|
||||
Но если нажать на второй, то фокусировки не произойдёт.
|
||||
But if you click the second one, there's no focus.
|
||||
|
||||
```html run autorun
|
||||
<input value="Фокус работает" onfocus="this.value=''">
|
||||
<input *!*onmousedown="return false"*/!* onfocus="this.value=''" value="Кликни меня">
|
||||
<input value="Focus works" onfocus="this.value=''">
|
||||
<input *!*onmousedown="return false"*/!* onfocus="this.value=''" value="Click me">
|
||||
```
|
||||
|
||||
Это потому, что отменено стандартное действие при `onmousedown`.
|
||||
|
||||
...С другой стороны, во второй `<input>` можно перейти с первого нажатием клавиши `key:Tab`, и тогда фокусировка сработает. То есть, дело здесь именно в `onmousedown="return false"`.
|
||||
That's because the browser action is canceled on `mousedown`. The focusing is still possible if we use another way to enter the input. For instance, the `key:Tab` key to switch from the 1st input into the 2nd.
|
||||
````
|
||||
|
||||
## Особенности IE8-
|
||||
|
||||
В IE8- для отмены действия по умолчанию нужно назначить свойство `event.returnValue = false`.
|
||||
|
||||
Кроссбраузерный код для отмены действия по умолчанию:
|
||||
|
||||
```js
|
||||
element.onclick = function(event) {
|
||||
event = event || window.event;
|
||||
|
||||
if (event.preventDefault) { // если метод существует
|
||||
event.preventDefault(); // то вызвать его
|
||||
} else { // иначе вариант IE8-:
|
||||
event.returnValue = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Можно записать в одну строку:
|
||||
|
||||
```js no-beautify
|
||||
...
|
||||
event.preventDefault ? event.preventDefault() : (event.returnValue=false);
|
||||
...
|
||||
```
|
||||
|
||||
## Итого
|
||||
|
||||
- Браузер имеет встроенные действия при ряде событий -- переход по ссылке, отправка формы и т.п. Как правило, их можно отменить.
|
||||
- Есть два способа отменить действие по умолчанию: первый -- использовать `event.preventDefault()` (IE8-: `event.returnValue=false`), второй -- `return false` из обработчика. Второй способ работает только если обработчик назначен через `onсобытие`.
|
||||
## Summary
|
||||
|
||||
- Browser has default actions for many events -- following a link, submitting a form etc.
|
||||
- To prevent a default action -- use either `event.preventDefault()` or `return false`. The second method works only for handlers assigned with `on<event>`.
|
||||
|
|
|
@ -9,14 +9,13 @@
|
|||
<body>
|
||||
|
||||
<ul id="menu" class="menu">
|
||||
<li><a href="/php">PHP</a></li>
|
||||
<li><a href="/html">HTML</a></li>
|
||||
<li><a href="/javascript">JavaScript</a></li>
|
||||
<li><a href="/flash">Flash</a></li>
|
||||
<li><a href="/css">CSS</a></li>
|
||||
</ul>
|
||||
|
||||
<script src="menu.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
menu.onclick = function(event) {
|
||||
if (event.target.nodeName != 'A') return;
|
||||
|
||||
var href = event.target.getAttribute('href');
|
||||
let href = event.target.getAttribute('href');
|
||||
alert(href);
|
||||
|
||||
return false; // prevent url change
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,49 +1,168 @@
|
|||
# Генерация событий на элементах
|
||||
# Custom events
|
||||
|
||||
Можно не только назначать обработчики на события, но и генерировать их самому.
|
||||
We can not only assign handlers, but also generate events form JavaScript.
|
||||
|
||||
Мы будем использовать это позже для реализации компонентной архитектуры, при которой элемент, представляющий собой, к примеру, меню, генерирует события, к этому меню относящиеся -- `select` (выбран пункт меню) или `open` (меню раскрыто), и другие.
|
||||
Custom events can be used to create "graphical components". For instance, a root element of the menu may trigger events telling what happens with the menu: `open` (menu open), `select` (an item is selected) and so on.
|
||||
|
||||
Кроме того, события можно генерировать для целей автоматического тестирования.
|
||||
Also we can generate built-in events like `click`, `mousedown` etc, that may be good for testing.
|
||||
|
||||
[cut]
|
||||
|
||||
## Конструктор Event
|
||||
## Event constructor
|
||||
|
||||
Вначале рассмотрим современный способ генерации событий, по стандарту [DOM 4](http://www.w3.org/TR/dom/#introduction-to-dom-events). Он поддерживается всеми браузерами, кроме IE11-. А далее рассмотрим устаревшие варианты, поддерживаемые IE.
|
||||
Events form a hierarchy, just like DOM element classes. The root is the built-in [Event](http://www.w3.org/TR/dom/#event) class.
|
||||
|
||||
Объект события в нём создаётся при помощи встроенного конструктора [Event](http://www.w3.org/TR/dom/#event).
|
||||
|
||||
Синтаксис:
|
||||
We can create `Event` objects like this:
|
||||
|
||||
```js
|
||||
var event = new Event(тип события[, флаги]);
|
||||
let event = new Event(event type[, flags]);
|
||||
```
|
||||
|
||||
Где:
|
||||
Arguments:
|
||||
|
||||
- *Тип события* -- может быть как своим, так и встроенным, к примеру `"click"`.
|
||||
- *Флаги* -- объект вида `{ bubbles: true/false, cancelable: true/false }`, где свойство `bubbles` указывает, всплывает ли событие, а `cancelable` -- можно ли отменить действие по умолчанию.
|
||||
- *event type* -- may be any string, like `"click"` or our own like `"hey-ho!"`.
|
||||
- *flags* -- the object with two optional properties:
|
||||
- `bubbles: true/false` -- if `true`, then the event bubbles.
|
||||
- `cancelable: true/false` -- if `true`, then the "default action" may be prevented. Later we'll see what it means for custom events.
|
||||
|
||||
Флаги по умолчанию: `{bubbles: false, cancelable: false}`.
|
||||
By default both flags are false: `{bubbles: false, cancelable: false}`.
|
||||
|
||||
## Метод dispatchEvent
|
||||
## The method dispatchEvent
|
||||
|
||||
Затем, чтобы инициировать событие, запускается `elem.dispatchEvent(event)`.
|
||||
After an event object is created, we should "run" it on an element using the call `elem.dispatchEvent(event)`.
|
||||
|
||||
При этом событие срабатывает наравне с браузерными, то есть обычные браузерные обработчики на него отреагируют. Если при создании указан флаг `bubbles`, то оно будет всплывать.
|
||||
Then handlers react on it as if it were a regular built-in event. If the event was created with the `bubbles` flag, then it bubbles.
|
||||
|
||||
При просмотре примера ниже кнопка обработчик `onclick` на кнопке сработает сам по себе, событие генерируется скриптом:
|
||||
In the example below the `click` event is initiated in JavaScript. The handler works same way as if the button was clicked:
|
||||
|
||||
```html run no-beautify
|
||||
<button id="elem" onclick="alert('Клик');">Автоклик</button>
|
||||
<button id="elem" onclick="alert('Click!');">Autoclick</button>
|
||||
|
||||
<script>
|
||||
var event = new Event("click");
|
||||
let event = new Event("click");
|
||||
elem.dispatchEvent(event);
|
||||
</script>
|
||||
```
|
||||
|
||||
```smart header="event.isTrusted"
|
||||
There is a way to tell a "real" user event from a script-generated one.
|
||||
|
||||
The property `event.isTrusted` is `true` for events that come from real user actions and `false` for script-generated events.
|
||||
```
|
||||
|
||||
## Bubbling example
|
||||
|
||||
We can create a bubbling event with the name `"hello"` and catch it on `document`.
|
||||
|
||||
All we need is to set `bubbles` to `true`:
|
||||
|
||||
```html run no-beautify
|
||||
<h1 id="elem">Hello from the script!</h1>
|
||||
|
||||
<script>
|
||||
// catch on document...
|
||||
document.addEventListener("hello", function(event) { // (1)
|
||||
alert("Hello from " + event.target.tagName); // Hello from H1
|
||||
});
|
||||
|
||||
// ...dispatch on elem!
|
||||
let event = new Event("hello", {bubbles: true}); // (2)
|
||||
elem.dispatchEvent(event);
|
||||
</script>
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
1. We must use `addEventListener` for our custom events, because `on<event>` only exists for built-in events.
|
||||
2. Must set `bubbles`, otherwise the event won't bubble up.
|
||||
|
||||
The bubbling mechanics is the same for built-in (`click`) and custom (`hello`) events. There are also capturing and bubbling stages.
|
||||
|
||||
## MouseEvent, KeyboardEvent and others
|
||||
|
||||
Here's a short list of classes for UI Events from the [UI Event specification](https://www.w3.org/TR/uievents):
|
||||
|
||||
- `UIEvent`
|
||||
- `FocusEvent`
|
||||
- `MouseEvent`
|
||||
- `WheelEvent`
|
||||
- `KeyboardEvent`
|
||||
- ...
|
||||
|
||||
We should use them instead of `new Event` if we want to create such events. For instance, `new MouseEvent("click")`.
|
||||
|
||||
The right constructor allows to specify standard properties for that type of event.
|
||||
|
||||
Like `clientX/clientY` for a mouse event:
|
||||
|
||||
```js run
|
||||
let event = new MouseEvent("click", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: 100,
|
||||
clientY: 100
|
||||
});
|
||||
|
||||
*!*
|
||||
alert(event.clientX); // 100
|
||||
*/!*
|
||||
```
|
||||
|
||||
Please note: the generic `Event` constructor does not allow that.
|
||||
|
||||
Let's try:
|
||||
|
||||
```js run
|
||||
let event = new Event("click", {
|
||||
bubbles: true, // only bubbles and cancelable
|
||||
cancelable: true, // work in the Event constructor
|
||||
clientX: 100,
|
||||
clientY: 100
|
||||
});
|
||||
|
||||
*!*
|
||||
alert(event.clientX); // undefined, the unknown property is ignored!
|
||||
*/!*
|
||||
```
|
||||
|
||||
Technically, we can work around that by assigning directly `event.clientX=100` after creation. So that's a matter of convenience and following the rules. Browser-generated events always have the right type.
|
||||
|
||||
The full list of properties for different UI events is in the specification, for instance [MouseEvent](https://www.w3.org/TR/uievents/#mouseevent).
|
||||
|
||||
## Custom events
|
||||
|
||||
For our own, custom events like `"hello"` we should use `new CustomEvent`. Technically [CustomEvent](https://dom.spec.whatwg.org/#customevent) is the same as `Event`, with one exception.
|
||||
|
||||
In the second argument (object) we can add an additional property `detail` for any custom information that we want to pass with the event.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run
|
||||
<h1 id="elem">Hello for John!</h1>
|
||||
|
||||
<script>
|
||||
elem.addEventListener("hello", function(event) {
|
||||
alert(*!*event.detail.name*/!*);
|
||||
});
|
||||
|
||||
let event = new CustomEvent("hello", {
|
||||
*!*
|
||||
detail: { name: "John" }
|
||||
*/!*
|
||||
});
|
||||
|
||||
elem.dispatchEvent(event);
|
||||
</script>
|
||||
```
|
||||
|
||||
The `detail` can be anything. Once again, technically we can assign any properties into a regular `Event` object after its creation. But `CustomEvent` clearly states that the event is not built-in, but our own. And reserves the special `detail` field, so that we can write to it safely, without conflicts with standard event properties.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Отмена действия по умолчанию
|
||||
|
||||
На сгенерированном событии, как и на встроенном браузерном, обработчик может вызвать метод `event.preventDefault()`. Тогда `dispatchEvent` возвратит `false`.
|
||||
|
@ -92,324 +211,9 @@ var event = new Event(тип события[, флаги]);
|
|||
</script>
|
||||
```
|
||||
|
||||
```smart header="Как отличить реальное нажатие от скриптового?"
|
||||
В целях безопасности иногда хорошо бы знать -- инициировано ли действие посетителем или это кликнул скрипт.
|
||||
|
||||
Единственный способ, которым код может отличить реальное нажатие от программного, является проверка свойства `event.isTrusted`.
|
||||
|
||||
Оно на момент написания статьи поддерживается IE и Firefox и равно `true`, если посетитель кликнул сам, и всегда `false` -- если событие инициировал скрипт.
|
||||
```
|
||||
|
||||
## Другие свойства событий
|
||||
|
||||
При создании события браузер автоматически ставит следующие свойства:
|
||||
|
||||
- `isTrusted: false` -- означает, что событие сгенерировано скриптом, это свойство изменить невозможно.
|
||||
- `target: null` -- это свойство ставится автоматически позже при `dispatchEvent`.
|
||||
- `type: тип события` -- первый аргумент `new Event`.
|
||||
- `bubbles`, `cancelable` -- по второму аргументу `new Event`.
|
||||
|
||||
Другие свойства события, если они нужны, например координаты для события мыши -- можно присвоить в объект события позже, например:
|
||||
|
||||
```js no-beautify
|
||||
var event = new Event("click", {bubbles: true, cancelable: false});
|
||||
event.clientX = 100;
|
||||
event.clientY = 100;
|
||||
```
|
||||
|
||||
## Пример со всплытием
|
||||
|
||||
Сгенерируем совершенно новое событие `"hello"` и поймаем его на `document`.
|
||||
|
||||
Всё, что для этого нужно -- это флаг `bubbles`:
|
||||
|
||||
```html run no-beautify
|
||||
<h1 id="elem">Привет от скрипта!</h1>
|
||||
|
||||
<script>
|
||||
document.addEventListener("hello", function(event) { // (1)
|
||||
alert("Привет");
|
||||
event.preventDefault(); // (2)
|
||||
}, false);
|
||||
|
||||
var event = new Event("hello", {bubbles: true, cancelable: true}); // (3)
|
||||
if (elem.dispatchEvent(event) === false) {
|
||||
alert('Событие было отменено preventDefault');
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
Обратите внимание:
|
||||
|
||||
1. Обработчик события `hello` стоит на `document`. Мы его поймаем на всплытии.
|
||||
2. Вызов `event.preventDefault()` приведёт к тому, что `dispatchEvent` вернёт `false`.
|
||||
3. Чтобы событие всплывало и его можно было отменить, указан второй аргумент `new Event`.
|
||||
|
||||
Никакой разницы между встроенными событиями (`click`) и своими (`hello`) здесь нет, их можно сгенерировать и запустить совершенно одинаково.
|
||||
|
||||
## Конструкторы MouseEvent, KeyboardEvent и другие
|
||||
|
||||
Для некоторых конкретных типов событий есть свои, специфические, конструкторы.
|
||||
|
||||
Вот список конструкторов для различных событий интерфейса которые можно найти в спецификации [UI Event](http://www.w3.org/TR/uievents/):
|
||||
|
||||
- `UIEvent`
|
||||
- `FocusEvent`
|
||||
- `MouseEvent`
|
||||
- `WheelEvent`
|
||||
- `KeyboardEvent`
|
||||
- `CompositionEvent`
|
||||
|
||||
Вместо `new Event("click")` можно вызвать `new MouseEvent("click")`.
|
||||
|
||||
**Специфический конструктор позволяет указать стандартные свойства для данного типа события.**
|
||||
|
||||
Например, `clientX/clientY` для события мыши:
|
||||
|
||||
```js run
|
||||
var e = new MouseEvent("click", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: 100,
|
||||
clientY: 100
|
||||
});
|
||||
|
||||
*!*
|
||||
alert( e.clientX ); // 100
|
||||
*/!*
|
||||
```
|
||||
|
||||
Это нельзя было бы сделать с обычным конструктором `Event`:
|
||||
|
||||
```js run
|
||||
var e = new Event("click", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: 100,
|
||||
clientY: 100
|
||||
});
|
||||
|
||||
*!*
|
||||
alert( e.clientX ); // undefined, свойство не присвоено!
|
||||
*/!*
|
||||
```
|
||||
|
||||
Обычный конструктор `Event` не знает про "мышиные" свойства, поэтому их игнорирует.
|
||||
|
||||
Впрочем, использование конкретного конструктора не является обязательным, можно обойтись `Event`, а свойства записать в объект отдельно, после конструктора. Здесь это скорее вопрос удобства и желания следовать правилам. События, которые генерирует браузер, всегда имеют правильный тип.
|
||||
|
||||
Полный список свойств по типам событий вы найдёте в спецификации, например для `MouseEvent`: [MouseEvent Constructor](http://www.w3.org/TR/uievents/#constructor-mouseevent).
|
||||
|
||||
## Свои события
|
||||
|
||||
Для генерации своих, нестандартных, событий, хоть и можно использовать конструктор `Event`, но существует и специфический конструктор [CustomEvent](http://www.w3.org/TR/dom/#customevent).
|
||||
|
||||
Технически, он абсолютно идентичен `Event`, кроме небольшой детали: у второго аргумента-объекта есть дополнительное свойство `detail`, в котором можно указывать информацию для передачи в событие.
|
||||
|
||||
Например:
|
||||
|
||||
```html run
|
||||
<h1 id="elem">Привет для Васи!</h1>
|
||||
|
||||
<script>
|
||||
elem.addEventListener("hello", function(event) {
|
||||
alert( *!*event.detail.name*/!* );
|
||||
}, false);
|
||||
|
||||
var event = new CustomEvent("hello", {
|
||||
*!*
|
||||
detail: { name: "Вася" }
|
||||
*/!*
|
||||
});
|
||||
|
||||
elem.dispatchEvent(event);
|
||||
</script>
|
||||
```
|
||||
|
||||
Надо сказать, что никто не мешает и в обычное `Event` записать любые свойства. Но `CustomEvent` более явно говорит, что событие не встроенное, а своё, и выделяет отдельно "информационное" поле `detail`, в которое можно записать что угодно без конфликта со стандартными свойствами объекта.
|
||||
|
||||
## Старое API для IE9+
|
||||
|
||||
Способ генерации событий, описанный выше, не поддерживается в IE11-, там нужен другой, более старый способ, описанный в стандарте [DOM 3 Events](http://www.w3.org/TR/DOM-Level-3-Events).
|
||||
|
||||
В нём была предусмотрена [иерархия событий](http://www.w3.org/TR/DOM-Level-3-Events/#event-interfaces), с различными методами инициализации.
|
||||
|
||||
Она поддерживается как современными браузерами, так и IE9+. Там используется немного другой синтаксис, но по возможностям -- всё то же самое, что и в современном стандарте.
|
||||
|
||||
Можно использовать этот немного устаревший способ, если нужно поддерживать IE9+. Далее мы на его основе создадим полифилл.
|
||||
|
||||
Объект события создаётся вызовом `document.createEvent`:
|
||||
|
||||
```js
|
||||
var event = document.createEvent(eventInterface);
|
||||
```
|
||||
|
||||
Аргументы:
|
||||
|
||||
- `eventInterface` -- это тип события, например `MouseEvent`, `FocusEvent`, `KeyboardEvent`. В [секции 5 DOM 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#events-module) есть подробный список, какое событие к какому интерфейсу относится.
|
||||
|
||||
На практике можно всегда использовать самый общий интерфейс: `document.createEvent("Event")`.
|
||||
|
||||
Далее событие нужно инициализировать:
|
||||
|
||||
```js
|
||||
event.initEvent(type, boolean bubbles, boolean cancelable);
|
||||
```
|
||||
|
||||
Аргументы:
|
||||
|
||||
- `type` -- тип события, например `"click"`.
|
||||
- `bubbles` -- всплывает ли событие.
|
||||
- `cancelable` -- можно ли отменить событие.
|
||||
|
||||
Эти два кода аналогичны:
|
||||
|
||||
```js
|
||||
// современный стандарт
|
||||
var event = new Event("click", {
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
// старый стандарт
|
||||
var event = document.createEvent("Event");
|
||||
event.initEvent("click", true, true);
|
||||
```
|
||||
|
||||
Единственная разница -- старый стандарт поддерживается IE9+.
|
||||
|
||||
Этот пример с событием `hello` будет работать во всех браузерах, кроме IE8-:
|
||||
|
||||
```html run
|
||||
<h1 id="elem">Привет от скрипта!</h1>
|
||||
|
||||
<script>
|
||||
document.addEventListener("hello", function(event) {
|
||||
alert( "Привет" );
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
|
||||
*!*
|
||||
var event = document.createEvent("Event");
|
||||
event.initEvent("hello", true, true);
|
||||
*/!*
|
||||
|
||||
if (elem.dispatchEvent(event) === false) {
|
||||
alert( 'Событие было отменено preventDefault' );
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
````smart header="`initMouseEvent`, `initKeyboardEvent` и другие..."
|
||||
У конкретных типов событий, например `MouseEvent`, `KeyboardEvent`, есть методы, которые позволяют указать стандартные свойства.
|
||||
|
||||
Они называются по аналогии: `initMouseEvent`, `initKeyboardEvent`.
|
||||
|
||||
Их можно использовать вместо базового `initEvent`, если хочется, чтобы свойства событий соответствовали встроенным браузерным.
|
||||
|
||||
Выглядят они немного страшновато, например (взято из [спецификации](http://www.w3.org/TR/DOM-Level-3-Events/#idl-interface-MouseEvent-initializers)):
|
||||
|
||||
```js
|
||||
void initMouseEvent(
|
||||
DOMString typeArg, // тип
|
||||
boolean bubblesArg, // всплывает?
|
||||
boolean cancelableArg, // можно отменить?
|
||||
AbstractView ? viewArg, // объект window, null означает текущее окно
|
||||
long detailArg, // свойство detail и другие...
|
||||
long screenXArg,
|
||||
long screenYArg,
|
||||
long clientXArg,
|
||||
long clientYArg,
|
||||
boolean ctrlKeyArg,
|
||||
boolean altKeyArg,
|
||||
boolean shiftKeyArg,
|
||||
boolean metaKeyArg,
|
||||
unsigned short buttonArg,
|
||||
EventTarget ? relatedTargetArg);
|
||||
};
|
||||
```
|
||||
|
||||
Для инициализации мышиного события нужно обязательно указать *все* аргументы, например:
|
||||
|
||||
```html run
|
||||
<button id="elem">Автоклик</button>
|
||||
|
||||
<script>
|
||||
elem.onclick = function(e) {
|
||||
alert( 'Клик на координатах ' + e.clientX + ':' + e.clientY );
|
||||
};
|
||||
|
||||
var event = document.createEvent("MouseEvent");
|
||||
*!*
|
||||
event.initMouseEvent("click", true, true, null, 0, 0, 0, 100, 100, true, true, true, null, 1, null);
|
||||
*/!*
|
||||
elem.dispatchEvent(event);
|
||||
</script>
|
||||
```
|
||||
|
||||
Браузер, по стандарту, может сгенерировать отсутствующие свойства самостоятельно, например `pageX`, но это нужно проверять в конкретных случаях, иногда это не работает или работает некорректно, так что лучше указать все.
|
||||
````
|
||||
|
||||
## Полифилл 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()`.
|
||||
|
||||
Пример с ними для IE8:
|
||||
|
||||
```html run
|
||||
<button id="elem">Автоклик</button>
|
||||
|
||||
<script>
|
||||
document.body.onclick = function() {
|
||||
alert( "Клик, event.type=" + event.type );
|
||||
return false;
|
||||
};
|
||||
|
||||
*!*
|
||||
var event = document.createEventObject();
|
||||
if (!elem.fireEvent("onclick", event)) {
|
||||
alert( 'Событие было отменено' );
|
||||
}
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
**При помощи `fireEvent` можно сгенерировать только встроенные события.**
|
||||
|
||||
Если указать `"hello"` вместо `"onclick"` в примере выше -- будет ошибка.
|
||||
|
||||
Параметры `bubbles` и `cancelable` настраивать нельзя, браузер использует стандартные для данного типа событий.
|
||||
|
||||
Существуют полифиллы для генерации произвольных событий и для IE8-, но они, по сути, полностью подменяют встроенную систему обработки событий браузером. И кода это требует тоже достаточно много.
|
||||
|
||||
Альтернатива -- фреймворк, например jQuery, который также реализует свою мощную систему работы с событиями, доступную через методы jQuery.
|
||||
|
||||
## Итого
|
||||
|
||||
|
@ -426,4 +230,3 @@ try {
|
|||
- Либо как явный и грубый хак, чтобы заставить работать сторонние библиотеки, в которых не предусмотрены другие средства взаимодействия.
|
||||
- Либо для автоматического тестирования, чтобы скриптом "нажать на кнопку" и посмотреть, произошло ли нужное действие.
|
||||
- Либо при создании своих "элементов интерфейса". Например, никто не мешает при помощи JavaScript создать из `<div class="calendar">` красивый календарь и генерировать на нём событие `change` при выборе даты. Эту тему мы разовьём позже.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue