renovations
This commit is contained in:
parent
66e2f0919d
commit
25fc5d8650
19 changed files with 268 additions and 88 deletions
|
@ -1,15 +1,15 @@
|
|||
# Порядок обработки событий
|
||||
|
||||
События могут возникать не только по очереди, но и пачкой, по многу сразу. Возможно и такое, что во время обработки одного события возникают другие.
|
||||
События могут возникать не только по очереди, но и "пачкой" по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для `onclick` -- посетитель провёл мышкой, а это уже `mousemove`.
|
||||
|
||||
Здесь и далее, очень важно понимать, как браузер обычно работает с событиями и важные исключения из этого правила. Это мы и разберём в этой главе.
|
||||
Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила.
|
||||
|
||||
[cut]
|
||||
## Главный поток
|
||||
|
||||
В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM.
|
||||
|
||||
Он выполняет команды последовательно и блокируется при выводе модальных окон, таких как `alert`.
|
||||
Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как `alert`.
|
||||
|
||||
|
||||
[smart header="Дополнительные потоки тоже есть"]
|
||||
|
@ -21,9 +21,9 @@
|
|||
[smart header="Web Workers"]
|
||||
Существует спецификация <a href="http://www.w3.org/TR/workers/">Web Workers</a>, которая позволяет запускать дополнительные JavaScript-процессы(workers).
|
||||
|
||||
Они могут обмениваться сообщениями с главным процессом, но их переменные полностью независимы.
|
||||
Они могут обмениваться сообщениями с главным процессом, но у них свои переменные, и работают они также сами по себе.
|
||||
|
||||
В частности, дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
|
||||
Такие дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
|
||||
[/smart]
|
||||
|
||||
## Очередь событий
|
||||
|
@ -36,18 +36,17 @@
|
|||
|
||||
**Когда происходит событие, оно попадает в очередь.**
|
||||
|
||||
Внутри браузера существует главный внутренний цикл, который проверяет очередь и обрабатывает события, запускает соответствующие обработчики и т.п.
|
||||
Внутри браузера непрерывно работает "главный внутренний цикл", который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п.
|
||||
|
||||
**Иногда события добавляются в очередь сразу пачкой.**
|
||||
|
||||
Например, при клике на элементе генерируется несколько событий:
|
||||
<ol>
|
||||
<li>Сначала `mousedown` -- нажата кнопка мыши.</li>
|
||||
<li>Затем `mouseup` -- кнопка мыши отпущена.</li>
|
||||
<li>Так как это было над одним элементом, то дополнительно генерируется `click`</li>
|
||||
<li>Затем `mouseup` -- кнопка мыши отпущена и, так как это было над одним элементом, то дополнительно генерируется `click` (два события сразу).</li>
|
||||
</ol>
|
||||
|
||||
|
||||
[online]
|
||||
В действии:
|
||||
|
||||
```html
|
||||
|
@ -61,14 +60,17 @@
|
|||
area.onclick = function(e) { this.value += "click\n"; this.scrollTop = 1e9; };
|
||||
</script>
|
||||
```
|
||||
[/online]
|
||||
|
||||
Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер сначала обработает первое, а потом -- второе.
|
||||
Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер обработает их строго одно за другим: `mousedown` -> `mouseup` -> `click`.
|
||||
|
||||
**При этом каждое событие из очереди обрабатывается полностью отдельно от других.**
|
||||
При этом каждое событие из очереди обрабатывается полностью отдельно от других.
|
||||
|
||||
## Вложенные (синхронные) события
|
||||
|
||||
В тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
|
||||
Обычно возникающие события "становятся в очередь".
|
||||
|
||||
Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
|
||||
|
||||
Рассмотрим в качестве примера событие `onfocus`.
|
||||
|
||||
|
@ -96,9 +98,9 @@
|
|||
</script>
|
||||
```
|
||||
|
||||
В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже. При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`.
|
||||
В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже.
|
||||
|
||||
**Событие `onfocus`, инициированное вызовом `text.focus()`, будет обработано синхронно, прямо сейчас, до завершения `onclick`.**
|
||||
При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`. Код обработчика `onfocus`, который при этом запустится, сработает синхронно, прямо сейчас, до завершения `onclick`.
|
||||
|
||||
```html
|
||||
<!--+ autorun -->
|
||||
|
@ -121,12 +123,13 @@
|
|||
</script>
|
||||
```
|
||||
|
||||
При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
|
||||
При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
|
||||
|
||||
**Так ведут себя все браузеры, кроме IE.**
|
||||
[warn header="Исключение в IE"]
|
||||
Так ведут себя все браузеры, кроме IE.
|
||||
|
||||
В нём событие `onfocus` -- всегда асинхронное, так что будет сначала полностью обработан клик, а потом -- фокус. В остальных -- фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу.
|
||||
|
||||
[/warn]
|
||||
|
||||
## Делаем события асинхронными через setTimeout(...,0)
|
||||
|
||||
|
@ -134,7 +137,7 @@
|
|||
|
||||
Можно добиться и этого.
|
||||
|
||||
Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика.
|
||||
Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика `onclick`.
|
||||
|
||||
Если это неудобно, можно запланировать `text.focus()` чуть позже через `setTimeout(..., 0)`, вот так
|
||||
|
||||
|
@ -173,3 +176,6 @@
|
|||
<li>Синхронными являются вложенные события, инициированные из кода.</li>
|
||||
<li>Чтобы сделать событие гарантированно асинхронным, используется вызов через `setTimeout(func, 0)`.</li>
|
||||
</ul>
|
||||
|
||||
Отложенный вызов через `setTimeout(func, 0)` используется не только в событиях, а вообще -- всегда, когда мы хотим, чтобы некая функция `func` сработала после того, как текущий скрипт завершится.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<li>Мяч после перелёта должен становиться центром ровно под курсор мыши, если это возможно без вылета за край поля.</li>
|
||||
<li>CSS-анимация не обязательна, но желательна.</li>
|
||||
<li>Мяч должен останавливаться у границ поля, ни в коем случае не вылетать за них.</li>
|
||||
<li>При прокрутке страницы ничего не должно ломаться.</li>
|
||||
<li>При прокрутке страницы с полем ничего не должно ломаться.</li>
|
||||
</ul>
|
||||
|
||||
Замечания:
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
Чтобы хорошо обработать событие, недостаточно знать о том, что это -- "клик" или "нажатие клавиши". Могут понадобиться детали: координаты курсора, введённый символ и другие, в зависимости от события.
|
||||
|
||||
**Детали произошедшего браузер записывает в "объект события", который передаётся первым аргументом в обработчик.**
|
||||
Детали произошедшего браузер записывает в "объект события", который передаётся первым аргументом в обработчик.
|
||||
[cut]
|
||||
|
||||
## Получение объекта события
|
||||
## Свойства объекта события
|
||||
|
||||
Пример ниже демонстрирует использования объекта события:
|
||||
Пример ниже демонстрирует использование объекта события:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
|
@ -15,6 +15,7 @@
|
|||
|
||||
<script>
|
||||
elem.onclick = function(*!*event*/!*) {
|
||||
// вывести тип события, элемент и координаты клика
|
||||
alert(event.type + " на " + event.currentTarget);
|
||||
alert(event.clientX + ":" + event.clientY);
|
||||
}
|
||||
|
@ -26,7 +27,7 @@
|
|||
<dt>`event.type`</dt>
|
||||
<dd>Тип события, в данном случае `click`</dd>
|
||||
<dt>`event.currentTarget`</dt>
|
||||
<dd>Элемент, на котором сработал обработчик -- то же, что и `this`, но бывают ситуации, когда обработчик является методом объекта и его `this` при помощи `bind` привязан к объекту, тогда `event.currentTarget` полезен.</dd>
|
||||
<dd>Элемент, на котором сработал обработчик. Значение -- в точности такое же, как и у `this`, но бывают ситуации, когда обработчик является методом объекта и его `this` при помощи `bind` привязан к этому объекту, тогда мы можем использовать `event.currentTarget`.</dd>
|
||||
<dt>`event.clientX / event.clientY`</dt>
|
||||
<dd>Координаты курсора в момент клика (относительно окна)</dd>
|
||||
</dl>
|
||||
|
@ -37,7 +38,7 @@
|
|||
При назначении обработчика в HTML, тоже можно использовать переменную `event`, это будет работать кросс-браузерно:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=auto -->
|
||||
<!--+ autorun height=60 -->
|
||||
<input type="button" onclick="*!*alert(event.type)*/!*" value="Тип события">
|
||||
```
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
Этот обработчик для `<div>` сработает, если вы кликните по вложенному тегу `<em>` или `<code>`:
|
||||
|
||||
```html
|
||||
<!--+ autorun height=auto -->
|
||||
<!--+ autorun height=60 -->
|
||||
<div onclick="alert('Обработчик для Div сработал!')">
|
||||
<em>Кликните на <code>EM</code>, сработает обработчик на <code>DIV</code></em>
|
||||
</div>
|
||||
|
@ -22,7 +22,11 @@
|
|||
Например, есть 3 вложенных элемента `FORM > DIV > P`, с обработчиком на каждом:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<!--+ run autorun -->
|
||||
<style>
|
||||
body * { margin: 10px; border: 1px solid blue; }
|
||||
</style>
|
||||
|
||||
<form onclick="alert('form')">FORM
|
||||
<div onclick="alert('div')">DIV
|
||||
<p onclick="alert('p')">P</p>
|
||||
|
@ -30,10 +34,12 @@
|
|||
</form>
|
||||
```
|
||||
|
||||
Всплытие гарантирует, что клик по внутреннему `P` вызовет обработчик `onclick` (если есть) сначала на самом `P`, затем на элементе `DIV` далее на элементе `FORM`, и так далее вверх по цепочке родителей до самого `document`.
|
||||
Всплытие гарантирует, что клик по внутреннему `<p>` вызовет обработчик `onclick` (если есть) сначала на самом `<p>`, затем на элементе `<div>` далее на элементе `<form>`, и так далее вверх по цепочке родителей до самого `document`.
|
||||
|
||||
<img src="event-order-bubbling.png" alt="Порядок всплытия событий">
|
||||
|
||||
Поэтому если в примере выше кликнуть на `P`, то последовательно выведутся `alert`: `p` -> `div` -> `form`.
|
||||
|
||||
Этот процесс называется *всплытием*, потому что события "всплывают" от внутреннего элемента вверх через родителей, подобно тому, как всплывает пузырек воздуха в воде.
|
||||
|
||||
[warn header="Всплывают *почти* все события."]
|
||||
|
@ -54,14 +60,20 @@
|
|||
<li>`this` -- это **текущий элемент**, до которого дошло всплытие, на нём сейчас выполняется обработчик.</li>
|
||||
</ul>
|
||||
|
||||
Например, если стоит только один обработчик `form.onclick`, то он "поймает" все клики внутри него. Где бы ни был клик внутри -- он всплывёт до элемента `<form>`, на котором сработает обработчик.
|
||||
Например, если стоит только один обработчик `form.onclick`, то он "поймает" все клики внутри формы. Где бы ни был клик внутри -- он всплывёт до элемента `<form>`, на котором сработает обработчик.
|
||||
|
||||
При этом:
|
||||
|
||||
<ul>
|
||||
<li>`event.target` будет содержать элемент, на котором произошёл клик.</li>
|
||||
<li>`this` (`=event.currentTarget`) всегда будет сама форма, так как обработчик сработал на ней.</li>
|
||||
<li>`event.target` будет содержать ссылку на конкретный элемент внутри формы, самый вложенный, на котором произошёл клик.</li>
|
||||
</ul>
|
||||
|
||||
[online]
|
||||
[example height=220 src="bubble-target"]
|
||||
[/online]
|
||||
|
||||
Возможна и ситуация, когда `event.target` и `this` -- один и тот же элемент, например если в форме нет других тегов и клик был на самом элементе `<form>`.
|
||||
|
||||
## Прекращение всплытия
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
<body>
|
||||
<link type="text/css" rel="stylesheet" href="example.css">
|
||||
|
||||
Клик выведет <code>target</code> и <code>this</code>:
|
||||
|
||||
<form id="form">FORM
|
||||
<div>DIV
|
||||
<p>P</p>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue