renovations
|
@ -75,9 +75,29 @@ setTimeout(function() {
|
||||||
|
|
||||||
Теперь код работает, так как `user` достаётся из замыкания.
|
Теперь код работает, так как `user` достаётся из замыкания.
|
||||||
|
|
||||||
|
Это решение также позволяет передать дополнительные аргументы:
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
//+ run
|
||||||
|
var user = {
|
||||||
|
firstName: "Вася",
|
||||||
|
sayHi: function(who) {
|
||||||
|
alert(this.firstName + ": Привет, " + who);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
*!*
|
||||||
|
setTimeout(function() {
|
||||||
|
user.sayHi("Петя"); // Вася: Привет, Петя
|
||||||
|
}, 1000);
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Но тут же появляется и уязвимое место в структуре кода!
|
Но тут же появляется и уязвимое место в структуре кода!
|
||||||
|
|
||||||
**А что, если до срабатывания `setTimeout` в переменную `user` будет записано другое значение? К примеру, какой-то другой пользователь... В этом случае вызов неожиданно будет совсем не тот!**
|
А что, если до срабатывания `setTimeout` (ведь есть целая секунда) в переменную `user` будет записано другое значение? К примеру, в другом месте кода будет присвоено `user=(другой пользователь)`... В этом случае вызов неожиданно будет совсем не тот!
|
||||||
|
|
||||||
Хорошо бы гарантировать правильность контекста.
|
Хорошо бы гарантировать правильность контекста.
|
||||||
|
|
||||||
|
@ -93,17 +113,26 @@ function bind(func, context) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Результатом вызова `bind(func, context)`, как видно из кода, является анонимная функция функция `(*)`, вот она отдельно:
|
Посмотрим, что она делает, как работает, на таком примере:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var oldSayHi = user.sayHi;
|
||||||
|
var sayHi = bind(oldSayHi, user);
|
||||||
|
```
|
||||||
|
|
||||||
|
Результатом `bind(oldSayHi, user)`, как видно из кода, будет анонимная функция `(*)`, вот она отдельно:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function() { // (*)
|
function() { // (*)
|
||||||
return func.apply(context, arguments);
|
return oldSayHi.apply(user, arguments);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Если её вызвать с какими-то аргументами, то она сама ничего не делает, а "передаёт вызов" в `func`. Здесь используется `apply`, чтобы вызвать `func` с теми же аргументами, которые получила эта анонимная функция и с контекстом `context`, который берётся из замыкания (был задан при вызове `bind`).
|
Она запишется в переменную `sayHi`.
|
||||||
|
|
||||||
Иными словами, в результате вызова `bind` мы получаем "функцию-обёртку", которая прозрачно передаёт вызов в `func`, с фиксированным контекстом `context`.
|
Далее, если её вызвать с какими-то аргументами, например `sayHi("Петя")`, то она "передаёт вызов" в `oldSayHi` -- используется `.apply(user, arguments)`, чтобы передать в качестве контекста `user` (он будет взят из замыкания) и текущие аргументы `arguments`.
|
||||||
|
|
||||||
|
Иными словами, в результате вызова `bind(func, context)` мы получаем "функцию-обёртку", которая прозрачно передаёт вызов в `func`, с теми же аргументами, но фиксированным контекстом `context`.
|
||||||
|
|
||||||
Пример с `bind`:
|
Пример с `bind`:
|
||||||
|
|
||||||
|
@ -127,8 +156,32 @@ setTimeout( bind(user.sayHi, user), 1000 );
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
Теперь всё в порядке! В `setTimeout` пошла обёртка, фиксирующая контекст.
|
Теперь всё в порядке!
|
||||||
|
|
||||||
|
Вызов `bind(user.sayHi, user)` возвращает такую функцию-обёртку, которая гарантированно вызовет `user.sayHi` в контексте `user`. В данном случае, через 1000мс.
|
||||||
|
|
||||||
|
Причём, если вызвать обёртку с аргументами -- они пойдут в `user.sayHi` без изменений, фиксирован лишь контекст.
|
||||||
|
|
||||||
|
```js
|
||||||
|
//+ run
|
||||||
|
var user = {
|
||||||
|
firstName: "Вася",
|
||||||
|
sayHi: function(who) {
|
||||||
|
alert(this.firstName + ": Привет, " + who);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var sayHi = bind(user.sayHi, user);
|
||||||
|
|
||||||
|
*!*
|
||||||
|
sayHi("Петя"); // Вася: Привет, Петя
|
||||||
|
sayHi("Маша"); // Вася: Привет, Маша
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
В примере выше продемонстрирована другая частая цель использования `bind` -- "привязать" функцию к контексту, чтобы в дальнейшем "не таскать за собой" объект, а просто вызывать `sayHi`.
|
||||||
|
|
||||||
|
Результат `bind` можно передавать в любое место кода, вызывать как обычную функцию, он "помнит" свой контекст.
|
||||||
|
|
||||||
## Решение 3: встроенный метод bind [#bind]
|
## Решение 3: встроенный метод bind [#bind]
|
||||||
|
|
||||||
|
@ -151,7 +204,7 @@ var wrapper = func.bind(context[, arg1, arg2...])
|
||||||
<dd>Если указаны аргументы `arg1, arg2...` -- они будут прибавлены к каждому вызову новой функции, причем встанут *перед* теми, которые указаны при вызове.</dd>
|
<dd>Если указаны аргументы `arg1, arg2...` -- они будут прибавлены к каждому вызову новой функции, причем встанут *перед* теми, которые указаны при вызове.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
Результат вызова `func.bind(context)` аналогичен вызову `bind(func, context)`, описанному выше. То есть, `wrapper` -- это обёртка, фиксирующая контекст и передающая вызовы в `func`. Также можно указать аргументы, тогда и они будут фиксированы, а новые будут уже за ними, но об этом чуть позже.
|
Результат вызова `func.bind(context)` аналогичен вызову `bind(func, context)`, описанному выше. То есть, `wrapper` -- это обёртка, фиксирующая контекст и передающая вызовы в `func`. Также можно указать аргументы, тогда и они будут фиксированы, но об этом чуть позже.
|
||||||
|
|
||||||
Пример со встроенным методом `bind`:
|
Пример со встроенным методом `bind`:
|
||||||
|
|
||||||
|
@ -166,13 +219,14 @@ var user = {
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
// setTimeout( bind(user.sayHi, user), 1000 );
|
// setTimeout( bind(user.sayHi, user), 1000 );
|
||||||
|
|
||||||
setTimeout( user.sayHi.bind(user), 1000 ); // аналог через встроенный метод
|
setTimeout( user.sayHi.bind(user), 1000 ); // аналог через встроенный метод
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
Получили простой и надёжный способ привязать контекст, причём даже встроенный в JavaScript.
|
Получили простой и надёжный способ привязать контекст, причём даже встроенный в JavaScript.
|
||||||
|
|
||||||
|
Далее мы будем использовать именно встроенный метод `bind`.
|
||||||
|
|
||||||
[smart header="Привязать всё: `bindAll`"]
|
[smart header="Привязать всё: `bindAll`"]
|
||||||
Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:
|
Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:
|
||||||
|
|
||||||
|
@ -236,8 +290,9 @@ alert( triple(5) ); // = mul(3, 5) = 15
|
||||||
|
|
||||||
При помощи `bind` мы можем получить из функции её "частный вариант" как самостоятельную функцию и дальше передать в `setTimeout` или сделать с ней что-то ещё.
|
При помощи `bind` мы можем получить из функции её "частный вариант" как самостоятельную функцию и дальше передать в `setTimeout` или сделать с ней что-то ещё.
|
||||||
|
|
||||||
|
Наш выигрыш в этом состоит в том, что эта самостоятельная функция, во-первых, имеет понятное имя (`double`, `triple`), а во-вторых, повторные вызовы позволяют не указывать каждый раз первый аргумент, он уже фиксирован благодаря `bind`.
|
||||||
|
|
||||||
## Функция дла задач
|
## Функция ask для задач
|
||||||
|
|
||||||
В задачах этого раздела предполагается, что объявлена следующая "функция вопросов" `ask`:
|
В задачах этого раздела предполагается, что объявлена следующая "функция вопросов" `ask`:
|
||||||
|
|
||||||
|
@ -251,7 +306,7 @@ function ask(question, answer, ok, fail) {
|
||||||
|
|
||||||
Её назначение -- задать вопрос `question` и, если ответ совпадёт с `answer`, то запустить функцию `ok()`, а иначе -- функцию `fail()`.
|
Её назначение -- задать вопрос `question` и, если ответ совпадёт с `answer`, то запустить функцию `ok()`, а иначе -- функцию `fail()`.
|
||||||
|
|
||||||
В реальном проекте она будет сложнее, вместо `alert/prompt` -- вывод красивого JavaScript-диалога с рамочками, кнопочками и так далее, но это нам сейчас не нужно.
|
Несмотря на внешнюю простоту, функции такого вида активно используются в реальных проектах. Конечно, они будут сложнее, вместо `alert/prompt` -- вывод красивого JavaScript-диалога с рамочками, кнопочками и так далее, но это нам сейчас не нужно.
|
||||||
|
|
||||||
Пример использования:
|
Пример использования:
|
||||||
|
|
||||||
|
@ -272,9 +327,24 @@ function die() {
|
||||||
|
|
||||||
## Итого
|
## Итого
|
||||||
|
|
||||||
Функции и контекст к JavaScript -- как шнурки и кроссовки. Если мы куда-то отправляем шнурки (например в `setTimeout`), то кроссовки сами за ними не побегут.
|
<ul>
|
||||||
|
<li>Функция сама по себе не запоминает контекст выполнения.</li>
|
||||||
|
<li>Чтобы гарантировать правильный контекст для вызова `obj.func()`, нужно использовать функцию-обёртку, задать её через анонимную функцию:
|
||||||
|
```js
|
||||||
|
setTimeout(function() {
|
||||||
|
obj.func();
|
||||||
|
})
|
||||||
|
```
|
||||||
|
</li>
|
||||||
|
<li>...Либо использовать `bind`:
|
||||||
|
|
||||||
Нужно либо передать их дополнительно, либо привязать одно к другому вызовом `bind`, либо завернуть в замыкание.
|
```js
|
||||||
|
setTimeout( obj.func.bind(obj) );
|
||||||
|
```
|
||||||
|
</li>
|
||||||
|
<li>Вызов `bind` часто используют для привязки функции к контексту, чтобы затем присвоить её в обычную переменную и вызывать уже без явного указания объекта.</li>
|
||||||
|
<li>Вызов `bind` также позволяет фиксировать первые аргументы функции ("каррировать" её), и таким образом из общей функции получить её "частные" варианты -- чтобы использовать их многократно без повтора одних и тех же аргументов каждый раз.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
[head]
|
[head]
|
||||||
<script>
|
<script>
|
||||||
|
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 170 KiB |
|
@ -0,0 +1,41 @@
|
||||||
|
В стандартном режиме IE8 можно получить текущую прокрутку так:
|
||||||
|
|
||||||
|
```js
|
||||||
|
//+ run
|
||||||
|
alert( document.documentElement.scrollTop );
|
||||||
|
```
|
||||||
|
|
||||||
|
Самым простым, но неверным было бы такое решение:
|
||||||
|
```js
|
||||||
|
//+ run
|
||||||
|
// "полифилл"
|
||||||
|
window.pageYOffset = document.documentElement.scrollTop;
|
||||||
|
|
||||||
|
// использование "полифилла"
|
||||||
|
alert( window.pageYOffset );
|
||||||
|
```
|
||||||
|
|
||||||
|
Код выше не учитывает текущую прокрутку. Он присваивает `window.pageYOffset` один раз и в дальнейшем, чтобы получить текущую прокрутку, нужно снова обратиться к `document.documentElement.scrollTop` не меняет его. А задача как раз -- сделать полифилл, то есть дать возможность использовать `window.pageYOffset` для получения текущего состояния прокрутки без "танцев бубном", так же как в современных браузерах.
|
||||||
|
|
||||||
|
Для этого создадим свойство через геттер.
|
||||||
|
|
||||||
|
В IE8 для DOM-объектов работает `Object.defineProperty`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
//+ run
|
||||||
|
// полифилл
|
||||||
|
Object.defineProperty(window, 'pageYOffset', {
|
||||||
|
get: function() {
|
||||||
|
return document.documentElement.scrollTop;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// использование полифилла
|
||||||
|
alert( window.pageYOffset );
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Полифилл для pageYOffset в IE8
|
||||||
|
|
||||||
|
[importance 3]
|
||||||
|
|
||||||
|
Обычно в IE8 не поддерживается свойство `pageYOffset`. Напишите полифилл для него.
|
||||||
|
|
||||||
|
При подключённом полифилле такой код должен работать в IE8:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// текущая прокрутка страницы в IE8
|
||||||
|
alert( window.pageYOffset );
|
||||||
|
```
|
|
@ -1,35 +1,50 @@
|
||||||
# Итого
|
# Итого
|
||||||
|
|
||||||
В этой главе кратко перечислены основные свойства и методы DOM, которые мы изучили.
|
В этой главе кратко перечислены основные свойства и методы DOM, которые мы изучили. Их уже довольно много.
|
||||||
|
|
||||||
Используйте её, чтобы получить быстрый итоговый обзор того, что изучали ранее.
|
Используйте её, чтобы по-быстрому вспомнить и прокрутить в голове то, что изучали ранее. Все ли эти свойства вам знакомы?
|
||||||
|
|
||||||
|
Кое-где стоит ограничение на версии IE, но на все свойства можно найти или сделать или найти полифилл, с которым их можно использовать везде.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
## Создание
|
## Создание
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>`document.createElement(tag)`</dt><dd>создать элемент с тегом `tag`</dd>
|
<dt>`document.createElement(tag)`</dt><dd>Создать элемент с тегом `tag`</dd>
|
||||||
<dt>`document.createTextNode(txt)`</dt><dd>создать текстовый узел с текстом `txt`</dd>
|
<dt>`document.createTextNode(txt)`</dt><dd>Создать текстовый узел с текстом `txt`</dd>
|
||||||
<dt>`node.cloneNode(deep)`</dt><dd>клонировать существующий узел, если `deep=false`, то без потомков.</dd>
|
<dt>`node.cloneNode(deep)`</dt><dd>Клонировать существующий узел, если `deep=false`, то без потомков.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
## Свойства узлов
|
## Свойства узлов
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>`node.nodeType`</dt><dd>тип узла: 1(элемент) / 3(текст) / другие.</dd>
|
<dt>`node.nodeType`</dt><dd>Тип узла: 1(элемент) / 3(текст) / другие.</dd>
|
||||||
<dt>`elem.tagName`</dt><dd>тег элемента.</dd>
|
<dt>`elem.tagName`</dt><dd>Тег элемента.</dd>
|
||||||
<dt>`elem.innerHTML`</dt><dd>HTML внутри элемента.</dd>
|
<dt>`elem.innerHTML`</dt><dd>HTML внутри элемента.</dd>
|
||||||
<dt>`node.data`</dt><dd>содержимое любого узла любого типа, кроме элемента.</dd>
|
<dt>`elem.outerHTML`</dt><dd>Весь HTML элемента, включая сам тег. На запись использовать с осторожностью, так как не модифицирует элемент, а вставляет новый вместо него.</dd>
|
||||||
|
<dt>`node.data` / `node.nodeValue`</dt><dd>Содержимое узла любого типа, кроме элемента.</dd>
|
||||||
|
<dt>`node.textContent`</dt><dd>Текстовое содержимое узла, для элементов содержит текст с вырезанными тегами (IE9+).</dd>
|
||||||
|
<dt>`elem.hidden`</dt><dd>Если поставить `true`, то элемент будет скрыт (IE10+).</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
## Атрибуты
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>`elem.getAttribute(name)`, `elem.hasAttribute(name)`, `elem.setAttribute(name, value)`</dt>
|
||||||
|
<dd>Чтение атрибута, проверка наличия и запись.</dd>
|
||||||
|
<dt>`elem.dataset.*`</dt><dd>Значения атрибутов вида `data-*` (IE10+).</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
## Ссылки
|
## Ссылки
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>`document.documentElement`</dt>
|
<dt>`document.documentElement`</dt>
|
||||||
<dd>элемент `<HTML>`</dd>
|
<dd>Элемент `<HTML>`</dd>
|
||||||
<dt>`document.body`</dt>
|
<dt>`document.body`</dt>
|
||||||
<dd>элемент `<BODY>`</dd>
|
<dd>Элемент `<BODY>`</dd>
|
||||||
|
<dt>`document.head`</dt>
|
||||||
|
<dd>Элемент `<HEAD>` (IE9+)</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
По всем узлам:
|
По всем узлам:
|
||||||
|
@ -42,14 +57,15 @@
|
||||||
Только по элементам:
|
Только по элементам:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>`children`</li>
|
<li>`parentElement`</li>
|
||||||
<li>`nextElementSibling` `previousElementSibling`</li>
|
<li>`nextElementSibling` `previousElementSibling`</li>
|
||||||
<li>`firstElementChild` `lastElementChild`</li>
|
<li>`children`, `firstElementChild` `lastElementChild`</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
В IE8- из них работает только `children`, причём содержит не только элементы, но и комментарии (ошибка в браузере).
|
Все они IE9+, кроме `children`, который работает в IE8-, но содержит не только элементы, но и комментарии (ошибка в браузере).
|
||||||
|
|
||||||
### Таблицы
|
Дополнительно у некоторых типов элементов могут быть и другие ссылки, свойства, коллекции для навигации,
|
||||||
|
например для таблиц:
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>`table.rows[N]`</dt>
|
<dt>`table.rows[N]`</dt>
|
||||||
|
@ -62,17 +78,6 @@
|
||||||
<dd>номер ячейки в строке.</dd>
|
<dd>номер ячейки в строке.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
### Формы
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>`document.forms[N/name]`</dt>
|
|
||||||
<dd>форма по номеру/имени.</dd>
|
|
||||||
<dt>`form.elements[N/name]`</dt>
|
|
||||||
<dd>элемент формы по номеру/имени</dd>
|
|
||||||
<dt>`element.form`</dt>
|
|
||||||
<dd>форма для элемента.</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
## Поиск
|
## Поиск
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,16 +89,28 @@
|
||||||
<dt>`document.getElementById(id)`</dt>
|
<dt>`document.getElementById(id)`</dt>
|
||||||
<dd>По уникальному `id`</dd>
|
<dd>По уникальному `id`</dd>
|
||||||
<dt>`document.getElementsByName(name)`</dt>
|
<dt>`document.getElementsByName(name)`</dt>
|
||||||
<dd>По атрибуту `name`, в IE<10 работает только для элементов, где `name` предусмотрен стандартом.</dd>
|
<dd>По атрибуту `name`, в IE9- работает только для элементов, где `name` предусмотрен стандартом.</dd>
|
||||||
<dt>`*.getElementsByTagName(tag)`</dt>
|
<dt>`*.getElementsByTagName(tag)`</dt>
|
||||||
<dd>По тегу `tag`</dd>
|
<dd>По тегу `tag`</dd>
|
||||||
<dt>`*.getElementsByClassName(class)`</dt>
|
<dt>`*.getElementsByClassName(class)`</dt>
|
||||||
<dd>По классу, IE9+, корректно работает с элементами, у которых несколько классов.</dd>
|
<dd>По классу, IE9+, корректно работает с элементами, у которых несколько классов.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
При поддержки IE только версии 8 и выше, можно использовать только `querySelector/querySelectorAll`.
|
Если не нужно поддерживать IE7-, то можно использовать только `querySelector/querySelectorAll`. Методы `getElement*` работают быстрее (за счёт более оптимальной внутренней реализации), но в 99% случаев это различие очень небольшое и роли не играет.
|
||||||
|
|
||||||
|
Дополнительно есть методы:
|
||||||
|
<dl>
|
||||||
|
<dt>`elem.matches(css)`</dt>
|
||||||
|
<dd>Проверяет, подходит ли элемент под CSS-селектор.</dd.
|
||||||
|
<dt>`elem.closest(css)`</dt>
|
||||||
|
<dd>Ищет ближайший элемент сверху по иерархии DOM, подходящий под CSS-селектор. Первым проверяется сам `elem`. Этот элемент возвращается.</dd>
|
||||||
|
<dt>`elemA.contains(elemB)`</dt>
|
||||||
|
<dd>Возвращает `true`, если `elemA` является предком (содержит) `elemB`.</dd>
|
||||||
|
<dt>`elemA.compareDocumentPosition(elemB)`</dt>
|
||||||
|
<dd>Возвращает битовую маску, которая включает в себя отношение вложенности между `elemA` и `elemB`, а также -- какой из элементов появляется в DOM первым.</dd>
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
Для более старых IE нужен либо фреймворк, который сам умеет искать узлы по селектору, наподобие jQuery, либо пользоваться методами `get*`, все из которых, кроме `...ByClassName`, поддерживаются с древних времён.
|
|
||||||
|
|
||||||
## Изменение
|
## Изменение
|
||||||
|
|
||||||
|
@ -102,6 +119,19 @@
|
||||||
<li>`parent.removeChild(child)`</li>
|
<li>`parent.removeChild(child)`</li>
|
||||||
<li>`parent.insertBefore(newChild, refNode)`</li>
|
<li>`parent.insertBefore(newChild, refNode)`</li>
|
||||||
<li>`parent.insertAdjacentHTML("beforeBegin|afterBegin|beforeEnd|afterEnd", html)`</li>
|
<li>`parent.insertAdjacentHTML("beforeBegin|afterBegin|beforeEnd|afterEnd", html)`</li>
|
||||||
|
<li>`parent.insertAdjacentElement("beforeBegin|...|afterEnd", text)` (кроме FF)</li>
|
||||||
|
<li>`parent.insertAdjacentText("beforeBegin|...|afterEnd", text)` (кроме FF)</li>
|
||||||
|
<li>`document.write(...)`</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Скорее всего, понадобятся полифиллы для:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>`node.append(...nodes)`</li>
|
||||||
|
<li>`node.prepend(...nodes)`</li>
|
||||||
|
<li>`node.after(...nodes)`,</li>
|
||||||
|
<li>`node.before(...nodes)`</li>
|
||||||
|
<li>`node.replaceWith(...nodes)`</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
## Классы и стили
|
## Классы и стили
|
||||||
|
@ -110,7 +140,7 @@
|
||||||
<dt>`elem.className`</dt>
|
<dt>`elem.className`</dt>
|
||||||
<dd>Атрибут `class`</dt>
|
<dd>Атрибут `class`</dt>
|
||||||
<dt>`elem.classList.add(class) remove(class) toggle(class) contains(class)`</dt>
|
<dt>`elem.classList.add(class) remove(class) toggle(class) contains(class)`</dt>
|
||||||
<dd>Управление классами в HTML5, для IE8+ есть [эмуляция](https://github.com/eligrey/classList.js/blob/master/classList.js).</dd>
|
<dd>Управление классами, для IE9- есть [эмуляция](https://github.com/eligrey/classList.js/blob/master/classList.js).</dd>
|
||||||
<dt>`elem.style`</dt>
|
<dt>`elem.style`</dt>
|
||||||
<dd>Стили в атрибуте `style` элемента</dd>
|
<dd>Стили в атрибуте `style` элемента</dd>
|
||||||
<dt>`getComputedStyle(elem, "")`</dd>
|
<dt>`getComputedStyle(elem, "")`</dd>
|
||||||
|
|
|
@ -23,7 +23,7 @@ DOM позволяет делать что угодно с HTML-элементо
|
||||||
<dd>Вторая точка входа -- `document.body`, который соответствует тегу `<body>`.</dd>
|
<dd>Вторая точка входа -- `document.body`, который соответствует тегу `<body>`.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
В современных браузерах (кроме старых IE) также действует `document.head` -- прямой доступ к `<head>`
|
В современных браузерах (кроме IE8-) также есть `document.head` -- прямая ссылка на `<head>`
|
||||||
|
|
||||||
[warn header="Есть одна тонкость: `document.body` может быть равен `null`"]
|
[warn header="Есть одна тонкость: `document.body` может быть равен `null`"]
|
||||||
Нельзя получить доступ к элементу, которого еще не существует в момент выполнения скрипта.
|
Нельзя получить доступ к элементу, которого еще не существует в момент выполнения скрипта.
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
<defs></defs>
|
<defs></defs>
|
||||||
<g id="combined" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
<g id="combined" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||||
<g id="dom-links-elements.svg" sketch:type="MSArtboardGroup">
|
<g id="dom-links-elements.svg" sketch:type="MSArtboardGroup">
|
||||||
|
<rect id="Rectangle-8" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="131" y="5" width="194" height="24"></rect>
|
||||||
<text id="document.documentEle" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="bold" sketch:alignment="middle" fill="#8A704D">
|
<text id="document.documentEle" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="bold" sketch:alignment="middle" fill="#8A704D">
|
||||||
<tspan x="137.692383" y="22">document.documentElement <HTML></tspan>
|
<tspan x="137.692383" y="22">document.documentElement <HTML></tspan>
|
||||||
</text>
|
</text>
|
||||||
|
<rect id="Rectangle-7" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="165" y="73" width="113" height="24"></rect>
|
||||||
<text id="document.body-(если-" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="bold" sketch:alignment="middle" fill="#8A704D">
|
<text id="document.body-(если-" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="bold" sketch:alignment="middle" fill="#8A704D">
|
||||||
<tspan x="172.84375" y="90">document.body (если внутри body)</tspan>
|
<tspan x="172.84375" y="90">document.body (если внутри body)</tspan>
|
||||||
</text>
|
</text>
|
||||||
|
@ -21,8 +23,6 @@
|
||||||
<tspan x="221.183594" y="165" fill="#EE6B47">Element</tspan>
|
<tspan x="221.183594" y="165" fill="#EE6B47">Element</tspan>
|
||||||
</text>
|
</text>
|
||||||
<rect id="Rectangle-6" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="183" y="208" width="75" height="24"></rect>
|
<rect id="Rectangle-6" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="183" y="208" width="75" height="24"></rect>
|
||||||
<rect id="Rectangle-7" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="165" y="73" width="113" height="24"></rect>
|
|
||||||
<rect id="Rectangle-8" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="131" y="5" width="194" height="24"></rect>
|
|
||||||
<text id="<DIV>" sketch:type="MSTextLayer" font-family="Open Sans" font-size="14" font-weight="526" sketch:alignment="middle" fill="#8A704D">
|
<text id="<DIV>" sketch:type="MSTextLayer" font-family="Open Sans" font-size="14" font-weight="526" sketch:alignment="middle" fill="#8A704D">
|
||||||
<tspan x="201.051859" y="225"><DIV></tspan>
|
<tspan x="201.051859" y="225"><DIV></tspan>
|
||||||
</text>
|
</text>
|
||||||
|
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
@ -6,6 +6,9 @@
|
||||||
<defs></defs>
|
<defs></defs>
|
||||||
<g id="combined" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
<g id="combined" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||||
<g id="dom-links.svg" sketch:type="MSArtboardGroup">
|
<g id="dom-links.svg" sketch:type="MSArtboardGroup">
|
||||||
|
<rect id="Rectangle-9" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="153" y="2" width="113" height="24"></rect>
|
||||||
|
<rect id="Rectangle-7" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="153" y="136" width="113" height="24"></rect>
|
||||||
|
<rect id="Rectangle-8" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="119" y="68" width="194" height="24"></rect>
|
||||||
<text id="document" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="bold" sketch:alignment="middle" fill="#8A704D">
|
<text id="document" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="bold" sketch:alignment="middle" fill="#8A704D">
|
||||||
<tspan x="177.710938" y="18">document</tspan>
|
<tspan x="177.710938" y="18">document</tspan>
|
||||||
</text>
|
</text>
|
||||||
|
@ -25,9 +28,6 @@
|
||||||
<tspan x="172" y="228">parentNode</tspan>
|
<tspan x="172" y="228">parentNode</tspan>
|
||||||
</text>
|
</text>
|
||||||
<rect id="Rectangle-6" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="171" y="271" width="75" height="24"></rect>
|
<rect id="Rectangle-6" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="171" y="271" width="75" height="24"></rect>
|
||||||
<rect id="Rectangle-7" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="153" y="136" width="113" height="24"></rect>
|
|
||||||
<rect id="Rectangle-9" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="153" y="2" width="113" height="24"></rect>
|
|
||||||
<rect id="Rectangle-8" stroke="#E8C48E" stroke-width="4" fill="#FFF9EB" sketch:type="MSShapeGroup" x="119" y="68" width="194" height="24"></rect>
|
|
||||||
<text id="<DIV>" sketch:type="MSTextLayer" font-family="Open Sans" font-size="14" font-weight="526" sketch:alignment="middle" fill="#8A704D">
|
<text id="<DIV>" sketch:type="MSTextLayer" font-family="Open Sans" font-size="14" font-weight="526" sketch:alignment="middle" fill="#8A704D">
|
||||||
<tspan x="189.051859" y="288"><DIV></tspan>
|
<tspan x="189.051859" y="288"><DIV></tspan>
|
||||||
</text>
|
</text>
|
||||||
|
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
@ -260,12 +260,13 @@ alert( articles.length ); // 2, найдёт оба элемента
|
||||||
|
|
||||||
## closest
|
## closest
|
||||||
|
|
||||||
Метод `elem.closest(css)` ищет ближайшего предка, подходящего под CSS-селектор `css`.
|
Метод `elem.closest(css)` ищет ближайший элемент выше по иерархии DOM, подходящий под CSS-селектор `css`. Сам элемент тоже включается в поиск.
|
||||||
|
|
||||||
|
Иначе говоря, метод `closest` бежит от текущего элемента вверх по цепочке родителей и проверяет, подходит ли каждый элемент под CSS-селектор. Если подходит -- останавливается и возвращает его.
|
||||||
|
|
||||||
Он самый новый из методов, рассмотренных в этой главе, поэтому не все браузеры его поддерживают. Это, конечно, легко поправимо, как мы увидим позже в главе [](/dom-polyfill).
|
Он самый новый из методов, рассмотренных в этой главе, поэтому не все браузеры его поддерживают. Это, конечно, легко поправимо, как мы увидим позже в главе [](/dom-polyfill).
|
||||||
|
|
||||||
Пример использования:
|
Пример использования (браузер должен поддерживать `closest`):
|
||||||
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!--+ run -->
|
<!--+ run -->
|
||||||
|
@ -282,10 +283,15 @@ alert( articles.length ); // 2, найдёт оба элемента
|
||||||
<script>
|
<script>
|
||||||
var numberSpan = document.querySelector('.num');
|
var numberSpan = document.querySelector('.num');
|
||||||
|
|
||||||
// браузер должен поддерживать этот метод
|
// ближайший элемент сверху подходящий под селектор li
|
||||||
alert( numberSpan.closest('li').className ) // subchapter
|
alert( numberSpan.closest('li').className ) // subchapter
|
||||||
|
|
||||||
|
// ближайший элемент сверху подходящий под селектор .chapter
|
||||||
alert( numberSpan.closest('.chapter').tagName ) // LI
|
alert( numberSpan.closest('.chapter').tagName ) // LI
|
||||||
|
|
||||||
|
// ближайший элемент сверху, подходящий под селектор span
|
||||||
|
// это сам numberSpan, так как поиск включает в себя сам элемент
|
||||||
|
alert( numberSpan.closest('span') === numberSpan ) // true
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -372,7 +372,7 @@ div.classList.add('order-state-canceled');
|
||||||
|
|
||||||
Проще говоря, значение атрибута -- произвольная строка, значение класса -- это "есть" или "нет", поэтому естественно, что атрибуты "мощнее" и бывают удобнее классов как в JS так и в CSS.
|
Проще говоря, значение атрибута -- произвольная строка, значение класса -- это "есть" или "нет", поэтому естественно, что атрибуты "мощнее" и бывают удобнее классов как в JS так и в CSS.
|
||||||
|
|
||||||
## Свойство dataSet, data-атрибуты
|
## Свойство dataset, data-атрибуты
|
||||||
|
|
||||||
С помощью нестандартных атрибутов можно привязать к элементу данные, которые будут доступны в JavaScript.
|
С помощью нестандартных атрибутов можно привязать к элементу данные, которые будут доступны в JavaScript.
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
# Порядок обработки событий
|
# Порядок обработки событий
|
||||||
|
|
||||||
События могут возникать не только по очереди, но и пачкой, по многу сразу. Возможно и такое, что во время обработки одного события возникают другие.
|
События могут возникать не только по очереди, но и "пачкой" по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для `onclick` -- посетитель провёл мышкой, а это уже `mousemove`.
|
||||||
|
|
||||||
Здесь и далее, очень важно понимать, как браузер обычно работает с событиями и важные исключения из этого правила. Это мы и разберём в этой главе.
|
Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
## Главный поток
|
## Главный поток
|
||||||
|
|
||||||
В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM.
|
В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM.
|
||||||
|
|
||||||
Он выполняет команды последовательно и блокируется при выводе модальных окон, таких как `alert`.
|
Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как `alert`.
|
||||||
|
|
||||||
|
|
||||||
[smart header="Дополнительные потоки тоже есть"]
|
[smart header="Дополнительные потоки тоже есть"]
|
||||||
|
@ -21,9 +21,9 @@
|
||||||
[smart header="Web Workers"]
|
[smart header="Web Workers"]
|
||||||
Существует спецификация <a href="http://www.w3.org/TR/workers/">Web Workers</a>, которая позволяет запускать дополнительные JavaScript-процессы(workers).
|
Существует спецификация <a href="http://www.w3.org/TR/workers/">Web Workers</a>, которая позволяет запускать дополнительные JavaScript-процессы(workers).
|
||||||
|
|
||||||
Они могут обмениваться сообщениями с главным процессом, но их переменные полностью независимы.
|
Они могут обмениваться сообщениями с главным процессом, но у них свои переменные, и работают они также сами по себе.
|
||||||
|
|
||||||
В частности, дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
|
Такие дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
|
||||||
[/smart]
|
[/smart]
|
||||||
|
|
||||||
## Очередь событий
|
## Очередь событий
|
||||||
|
@ -36,18 +36,17 @@
|
||||||
|
|
||||||
**Когда происходит событие, оно попадает в очередь.**
|
**Когда происходит событие, оно попадает в очередь.**
|
||||||
|
|
||||||
Внутри браузера существует главный внутренний цикл, который проверяет очередь и обрабатывает события, запускает соответствующие обработчики и т.п.
|
Внутри браузера непрерывно работает "главный внутренний цикл", который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п.
|
||||||
|
|
||||||
**Иногда события добавляются в очередь сразу пачкой.**
|
**Иногда события добавляются в очередь сразу пачкой.**
|
||||||
|
|
||||||
Например, при клике на элементе генерируется несколько событий:
|
Например, при клике на элементе генерируется несколько событий:
|
||||||
<ol>
|
<ol>
|
||||||
<li>Сначала `mousedown` -- нажата кнопка мыши.</li>
|
<li>Сначала `mousedown` -- нажата кнопка мыши.</li>
|
||||||
<li>Затем `mouseup` -- кнопка мыши отпущена.</li>
|
<li>Затем `mouseup` -- кнопка мыши отпущена и, так как это было над одним элементом, то дополнительно генерируется `click` (два события сразу).</li>
|
||||||
<li>Так как это было над одним элементом, то дополнительно генерируется `click`</li>
|
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
[online]
|
||||||
В действии:
|
В действии:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
@ -61,14 +60,17 @@
|
||||||
area.onclick = function(e) { this.value += "click\n"; this.scrollTop = 1e9; };
|
area.onclick = function(e) { this.value += "click\n"; this.scrollTop = 1e9; };
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
[/online]
|
||||||
|
|
||||||
Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер сначала обработает первое, а потом -- второе.
|
Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер обработает их строго одно за другим: `mousedown` -> `mouseup` -> `click`.
|
||||||
|
|
||||||
**При этом каждое событие из очереди обрабатывается полностью отдельно от других.**
|
При этом каждое событие из очереди обрабатывается полностью отдельно от других.
|
||||||
|
|
||||||
## Вложенные (синхронные) события
|
## Вложенные (синхронные) события
|
||||||
|
|
||||||
В тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
|
Обычно возникающие события "становятся в очередь".
|
||||||
|
|
||||||
|
Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
|
||||||
|
|
||||||
Рассмотрим в качестве примера событие `onfocus`.
|
Рассмотрим в качестве примера событие `onfocus`.
|
||||||
|
|
||||||
|
@ -96,9 +98,9 @@
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже. При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`.
|
В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже.
|
||||||
|
|
||||||
**Событие `onfocus`, инициированное вызовом `text.focus()`, будет обработано синхронно, прямо сейчас, до завершения `onclick`.**
|
При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`. Код обработчика `onfocus`, который при этом запустится, сработает синхронно, прямо сейчас, до завершения `onclick`.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!--+ autorun -->
|
<!--+ autorun -->
|
||||||
|
@ -123,10 +125,11 @@
|
||||||
|
|
||||||
При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
|
При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
|
||||||
|
|
||||||
**Так ведут себя все браузеры, кроме IE.**
|
[warn header="Исключение в IE"]
|
||||||
|
Так ведут себя все браузеры, кроме IE.
|
||||||
|
|
||||||
В нём событие `onfocus` -- всегда асинхронное, так что будет сначала полностью обработан клик, а потом -- фокус. В остальных -- фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу.
|
В нём событие `onfocus` -- всегда асинхронное, так что будет сначала полностью обработан клик, а потом -- фокус. В остальных -- фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу.
|
||||||
|
[/warn]
|
||||||
|
|
||||||
## Делаем события асинхронными через setTimeout(...,0)
|
## Делаем события асинхронными через setTimeout(...,0)
|
||||||
|
|
||||||
|
@ -134,7 +137,7 @@
|
||||||
|
|
||||||
Можно добиться и этого.
|
Можно добиться и этого.
|
||||||
|
|
||||||
Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика.
|
Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика `onclick`.
|
||||||
|
|
||||||
Если это неудобно, можно запланировать `text.focus()` чуть позже через `setTimeout(..., 0)`, вот так
|
Если это неудобно, можно запланировать `text.focus()` чуть позже через `setTimeout(..., 0)`, вот так
|
||||||
|
|
||||||
|
@ -173,3 +176,6 @@
|
||||||
<li>Синхронными являются вложенные события, инициированные из кода.</li>
|
<li>Синхронными являются вложенные события, инициированные из кода.</li>
|
||||||
<li>Чтобы сделать событие гарантированно асинхронным, используется вызов через `setTimeout(func, 0)`.</li>
|
<li>Чтобы сделать событие гарантированно асинхронным, используется вызов через `setTimeout(func, 0)`.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
Отложенный вызов через `setTimeout(func, 0)` используется не только в событиях, а вообще -- всегда, когда мы хотим, чтобы некая функция `func` сработала после того, как текущий скрипт завершится.
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<li>Мяч после перелёта должен становиться центром ровно под курсор мыши, если это возможно без вылета за край поля.</li>
|
<li>Мяч после перелёта должен становиться центром ровно под курсор мыши, если это возможно без вылета за край поля.</li>
|
||||||
<li>CSS-анимация не обязательна, но желательна.</li>
|
<li>CSS-анимация не обязательна, но желательна.</li>
|
||||||
<li>Мяч должен останавливаться у границ поля, ни в коем случае не вылетать за них.</li>
|
<li>Мяч должен останавливаться у границ поля, ни в коем случае не вылетать за них.</li>
|
||||||
<li>При прокрутке страницы ничего не должно ломаться.</li>
|
<li>При прокрутке страницы с полем ничего не должно ломаться.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
Замечания:
|
Замечания:
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
Чтобы хорошо обработать событие, недостаточно знать о том, что это -- "клик" или "нажатие клавиши". Могут понадобиться детали: координаты курсора, введённый символ и другие, в зависимости от события.
|
Чтобы хорошо обработать событие, недостаточно знать о том, что это -- "клик" или "нажатие клавиши". Могут понадобиться детали: координаты курсора, введённый символ и другие, в зависимости от события.
|
||||||
|
|
||||||
**Детали произошедшего браузер записывает в "объект события", который передаётся первым аргументом в обработчик.**
|
Детали произошедшего браузер записывает в "объект события", который передаётся первым аргументом в обработчик.
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
## Получение объекта события
|
## Свойства объекта события
|
||||||
|
|
||||||
Пример ниже демонстрирует использования объекта события:
|
Пример ниже демонстрирует использование объекта события:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!--+ run -->
|
<!--+ run -->
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
elem.onclick = function(*!*event*/!*) {
|
elem.onclick = function(*!*event*/!*) {
|
||||||
|
// вывести тип события, элемент и координаты клика
|
||||||
alert(event.type + " на " + event.currentTarget);
|
alert(event.type + " на " + event.currentTarget);
|
||||||
alert(event.clientX + ":" + event.clientY);
|
alert(event.clientX + ":" + event.clientY);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +27,7 @@
|
||||||
<dt>`event.type`</dt>
|
<dt>`event.type`</dt>
|
||||||
<dd>Тип события, в данном случае `click`</dd>
|
<dd>Тип события, в данном случае `click`</dd>
|
||||||
<dt>`event.currentTarget`</dt>
|
<dt>`event.currentTarget`</dt>
|
||||||
<dd>Элемент, на котором сработал обработчик -- то же, что и `this`, но бывают ситуации, когда обработчик является методом объекта и его `this` при помощи `bind` привязан к объекту, тогда `event.currentTarget` полезен.</dd>
|
<dd>Элемент, на котором сработал обработчик. Значение -- в точности такое же, как и у `this`, но бывают ситуации, когда обработчик является методом объекта и его `this` при помощи `bind` привязан к этому объекту, тогда мы можем использовать `event.currentTarget`.</dd>
|
||||||
<dt>`event.clientX / event.clientY`</dt>
|
<dt>`event.clientX / event.clientY`</dt>
|
||||||
<dd>Координаты курсора в момент клика (относительно окна)</dd>
|
<dd>Координаты курсора в момент клика (относительно окна)</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
@ -37,7 +38,7 @@
|
||||||
При назначении обработчика в HTML, тоже можно использовать переменную `event`, это будет работать кросс-браузерно:
|
При назначении обработчика в HTML, тоже можно использовать переменную `event`, это будет работать кросс-браузерно:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!--+ autorun height=auto -->
|
<!--+ autorun height=60 -->
|
||||||
<input type="button" onclick="*!*alert(event.type)*/!*" value="Тип события">
|
<input type="button" onclick="*!*alert(event.type)*/!*" value="Тип события">
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
Этот обработчик для `<div>` сработает, если вы кликните по вложенному тегу `<em>` или `<code>`:
|
Этот обработчик для `<div>` сработает, если вы кликните по вложенному тегу `<em>` или `<code>`:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!--+ autorun height=auto -->
|
<!--+ autorun height=60 -->
|
||||||
<div onclick="alert('Обработчик для Div сработал!')">
|
<div onclick="alert('Обработчик для Div сработал!')">
|
||||||
<em>Кликните на <code>EM</code>, сработает обработчик на <code>DIV</code></em>
|
<em>Кликните на <code>EM</code>, сработает обработчик на <code>DIV</code></em>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,7 +22,11 @@
|
||||||
Например, есть 3 вложенных элемента `FORM > DIV > P`, с обработчиком на каждом:
|
Например, есть 3 вложенных элемента `FORM > DIV > P`, с обработчиком на каждом:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!--+ run -->
|
<!--+ run autorun -->
|
||||||
|
<style>
|
||||||
|
body * { margin: 10px; border: 1px solid blue; }
|
||||||
|
</style>
|
||||||
|
|
||||||
<form onclick="alert('form')">FORM
|
<form onclick="alert('form')">FORM
|
||||||
<div onclick="alert('div')">DIV
|
<div onclick="alert('div')">DIV
|
||||||
<p onclick="alert('p')">P</p>
|
<p onclick="alert('p')">P</p>
|
||||||
|
@ -30,10 +34,12 @@
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
|
||||||
Всплытие гарантирует, что клик по внутреннему `P` вызовет обработчик `onclick` (если есть) сначала на самом `P`, затем на элементе `DIV` далее на элементе `FORM`, и так далее вверх по цепочке родителей до самого `document`.
|
Всплытие гарантирует, что клик по внутреннему `<p>` вызовет обработчик `onclick` (если есть) сначала на самом `<p>`, затем на элементе `<div>` далее на элементе `<form>`, и так далее вверх по цепочке родителей до самого `document`.
|
||||||
|
|
||||||
<img src="event-order-bubbling.png" alt="Порядок всплытия событий">
|
<img src="event-order-bubbling.png" alt="Порядок всплытия событий">
|
||||||
|
|
||||||
|
Поэтому если в примере выше кликнуть на `P`, то последовательно выведутся `alert`: `p` -> `div` -> `form`.
|
||||||
|
|
||||||
Этот процесс называется *всплытием*, потому что события "всплывают" от внутреннего элемента вверх через родителей, подобно тому, как всплывает пузырек воздуха в воде.
|
Этот процесс называется *всплытием*, потому что события "всплывают" от внутреннего элемента вверх через родителей, подобно тому, как всплывает пузырек воздуха в воде.
|
||||||
|
|
||||||
[warn header="Всплывают *почти* все события."]
|
[warn header="Всплывают *почти* все события."]
|
||||||
|
@ -54,14 +60,20 @@
|
||||||
<li>`this` -- это **текущий элемент**, до которого дошло всплытие, на нём сейчас выполняется обработчик.</li>
|
<li>`this` -- это **текущий элемент**, до которого дошло всплытие, на нём сейчас выполняется обработчик.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
Например, если стоит только один обработчик `form.onclick`, то он "поймает" все клики внутри него. Где бы ни был клик внутри -- он всплывёт до элемента `<form>`, на котором сработает обработчик.
|
Например, если стоит только один обработчик `form.onclick`, то он "поймает" все клики внутри формы. Где бы ни был клик внутри -- он всплывёт до элемента `<form>`, на котором сработает обработчик.
|
||||||
|
|
||||||
|
При этом:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>`event.target` будет содержать элемент, на котором произошёл клик.</li>
|
|
||||||
<li>`this` (`=event.currentTarget`) всегда будет сама форма, так как обработчик сработал на ней.</li>
|
<li>`this` (`=event.currentTarget`) всегда будет сама форма, так как обработчик сработал на ней.</li>
|
||||||
|
<li>`event.target` будет содержать ссылку на конкретный элемент внутри формы, самый вложенный, на котором произошёл клик.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
[online]
|
||||||
[example height=220 src="bubble-target"]
|
[example height=220 src="bubble-target"]
|
||||||
|
[/online]
|
||||||
|
|
||||||
|
Возможна и ситуация, когда `event.target` и `this` -- один и тот же элемент, например если в форме нет других тегов и клик был на самом элементе `<form>`.
|
||||||
|
|
||||||
## Прекращение всплытия
|
## Прекращение всплытия
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
<body>
|
<body>
|
||||||
<link type="text/css" rel="stylesheet" href="example.css">
|
<link type="text/css" rel="stylesheet" href="example.css">
|
||||||
|
|
||||||
|
Клик выведет <code>target</code> и <code>this</code>:
|
||||||
|
|
||||||
<form id="form">FORM
|
<form id="form">FORM
|
||||||
<div>DIV
|
<div>DIV
|
||||||
<p>P</p>
|
<p>P</p>
|
||||||
|
|