Порядок обработки событий
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/event-loop.
События могут возникать не только по очереди, но и «пачкой» по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для onclick – посетитель нажал кнопку на клавиатуре (событие keydown ).
Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила.
Главный поток
В каждом окне выполняется только один главный поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM.
Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как alert .
Есть и другие, служебные потоки, например, для сетевых коммуникаций.
Поэтому скачивание файлов может продолжаться пока главный поток ждёт реакции на alert . Но управлять служебными потоками мы не можем.
Существует спецификация Web Workers, которая позволяет запускать дополнительные JavaScript-процессы(workers).
Они могут обмениваться сообщениями с главным процессом, но у них свои переменные, и работают они также сами по себе.
Такие дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
Очередь событий
Произошло одновременно несколько событий или во время работы одного случилось другое – как главному потоку обработать это?
Если главный поток прямо сейчас занят, то он не может срочно выйти из середины одной функции и прыгнуть в другую. А потом третью. Отладка при этом могла бы превратиться в кошмар, потому что пришлось бы разбираться с совместным состоянием нескольких функций сразу.
Поэтому используется альтернативный подход.
Когда происходит событие, оно попадает в очередь.
Внутри браузера непрерывно работает «главный внутренний цикл», который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п.
Иногда события добавляются в очередь сразу пачкой.
Например, при клике на элементе генерируется несколько событий:
- Сначала mousedown – нажата кнопка мыши.
- Затем mouseup – кнопка мыши отпущена и, так как это было над одним элементом, то дополнительно генерируется click (два события сразу).
Таким образом, при нажатии кнопки мыши в очередь попадёт событие mousedown , а при отпускании – сразу два события: mouseup и click . Браузер обработает их строго одно за другим: mousedown → mouseup → click .
При этом каждое событие из очереди обрабатывается полностью отдельно от других.
Вложенные (синхронные) события
Обычно возникающие события «становятся в очередь».
Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
Рассмотрим в качестве примера событие onfocus .
Пример: событие onfocus
Когда посетитель фокусируется на элементе, возникает событие onfocus . Обычно оно происходит, когда посетитель кликает на поле ввода, например:
При фокусе на поле оно изменит значение.
Но ту же фокусировку можно вызвать и явно, вызовом метода elem.focus() :
В главе Фокусировка: focus/blur мы познакомимся с этим событием подробнее, а пока – нажмите на кнопку в примере ниже.
При этом обработчик onclick вызовет метод focus() на текстовом поле text . Код обработчика onfocus , который при этом запустится, сработает синхронно, прямо сейчас, до завершения onclick .
При клике на кнопке в примере выше будет видно, что управление вошло в onclick , затем перешло в onfocus , затем вышло из onclick .
Так ведут себя все браузеры, кроме IE.
В нём событие onfocus – всегда асинхронное, так что будет сначала полностью обработан клик, а потом – фокус. В остальных – фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу.
Делаем события асинхронными через setTimeout(…,0)
А что, если мы хотим, чтобы сначала закончилась обработка onclick , а потом уже произошла обработка onfocus и связанные с ней действия?
Один вариант – просто переместить строку text.focus() вниз кода обработчика onclick .
Если это неудобно, можно запланировать text.focus() чуть позже через setTimeout(. 0) , вот так
Такой вызов обеспечит фокусировку через минимальный «тик» таймера, по стандарту равный 4 мс. Обычно такая задержка не играет роли, а необходимую асинхронность мы получили.
Итого
- JavaScript выполняется в едином потоке. Современные браузеры позволяют порождать подпроцессы Web Workers, они выполняются параллельно и могут отправлять/принимать сообщения, но не имеют доступа к DOM.
- Обычно события становятся в очередь и обрабатываются в порядке поступления, асинхронно, независимо друг от друга.
- Синхронными являются вложенные события, инициированные из кода.
- Чтобы сделать событие гарантированно асинхронным, используется вызов через setTimeout(func, 0) .
Отложенный вызов через setTimeout(func, 0) используется не только в событиях, а вообще – всегда, когда мы хотим, чтобы некая функция func сработала после того, как текущий скрипт завершится.
Объект события Event
Объект Event описывает событие, произошедшее на странице. Одной из причин возникновения событий являются действия пользователя, такие как клики мышкой Mouse Event или ввод с клавиатуры Keyboard Event . Существует множество различных событий с разным набором информации.
Обратите внимание на обзорную статью о событиях. В ней описываются примеры работы с событиями.
Пример
Скопировать ссылку «Пример» Скопировано
Самый простой и широко распространённый способ использования событий — это отслеживание срабатывания кликов по каким-либо элементам на странице.
При подписке на событие мы передаём обработчик, который будет вызван при каждом срабатывании события в браузере. В случае, когда происходит событие типа click , обработчик будет вызван с событием Mouse Event :
element.addEventListener('click', function (event) console.log(event)>)
element.addEventListener('click', function (event) console.log(event) >)
Как пишется
Скопировать ссылку «Как пишется» Скопировано
В этом материале мы рассматриваем базовый объект события, каждое событие может содержать дополнительные свойства в зависимости от его типа, но список ниже есть у всех.
Свойства
Скопировать ссылку «Свойства» Скопировано
- bubbles — является ли данное событие всплывающим.
- cancelable — является ли событие отменяемым.
- current Target — указывает на элемент, на котором установлен обработчик события.
- default Prevented — отменено ли поведение события по умолчанию.
- event Phase — указывает на фазу срабатывания события.
- is Trusted — указывает на происхождение события, будет в значении true , если событие инициировано действиями пользователя. false — если событие инициировано из кода с помощью dispatch Event ( ) .
- target — ссылка на объект, которым было инициировано событие. Например, если событие произошло на поле ввода, мы получим ссылку на этот DOM элемент.
- time Stamp — время возникновения события в миллисекундах.
- type — тип события.
Методы
Скопировать ссылку «Методы» Скопировано
- composed Path ( ) — вернёт массив элементов, на которых сработает событие.
- prevent Default ( ) — предотвращает дефолтное поведение события. Если вызвать этот метод на событии клика по ссылке, то переход по ссылке не произойдёт, но событие продолжит всплытие.
- stop Propagation ( ) — предотвращает всплытие события.
- stop Immediate Propagation ( ) — делает то же самое, что и stop Propagation , но в том числе предотвращает вызов обработчиков события, которые были установлены на этом же элементе.
Обработчики событий, установленные на элемент, вызываются по порядку их установки.
Как понять
Скопировать ссылку «Как понять» Скопировано
Работа JavaScript основана на событийной модели — это значит, что для того, чтобы запустить какой-либо код, должно произойти событие. Даже код, который был написан в файле и не привязан к какому-либо событию, будет обработан после того, как произойдёт событие, которое сообщит браузеру, что код был загружен.
Событие может быть создано по следующим причинам:
Примеры
Скопировать ссылку «Примеры» Скопировано
Системное событие
Скопировать ссылку «Системное событие» Скопировано
Системное событие инициируется DOM-окружением и является отражением какого-то события, произошедшего в операционной системе. Например, событие, что пользователь находится онлайн. То есть на наличие активного интернет-соединения.
Мы можем отслеживать состояние интернет-соединения и показывать сообщение, если оно пропало.
window.addEventListener('offline', function() alert('Отсутствует подключение к интернету')>)
window.addEventListener('offline', function() alert('Отсутствует подключение к интернету') >)
Программное событие
Скопировать ссылку «Программное событие» Скопировано
Событие может быть создано с помощью кода, поле is Trusted в таком событии будет содержать значение false , а значит, мы будем знать, что событие было вызвано не системно и не пользователем.
Создадим своё событие и вызовем его на window :
const myEvent = new CustomEvent('my-event', detail: spicy: 123, >,>) window.addEventListener('my-event', function(evt) console.log('В поле spicy:', evt.detail.spicy)>) window.dispatchEvent(myEvent)
const myEvent = new CustomEvent('my-event', detail: spicy: 123, >, >) window.addEventListener('my-event', function(evt) console.log('В поле spicy:', evt.detail.spicy) >) window.dispatchEvent(myEvent)
На практике
Скопировать ссылку «На практике» Скопировано
Павел Минеев советует
Скопировать ссылку «Павел Минеев советует» Скопировано
🛠 В событии есть два похожих поля: target и current Target . Их отличие легко увидеть на примере.
Моя кнопочка
button class="button" type="button"> span>Моя кнопочкаspan> button>
document.querySelector('.button').addEventListener('click', function (event) console.log('Событие инициировано на', event.target) console.log('Событие поймано на', event.currentTarget)>)
document.querySelector('.button').addEventListener('click', function (event) console.log('Событие инициировано на', event.target) console.log('Событие поймано на', event.currentTarget) >)
current Target всегда будет элементом, к которому привязан обработчик, то есть элементом, на котором вызывался add Event Listener ( ) .
target — это элемент, на котором произошло событие. Оно может не совпадать с current Target , потому что большинство событий всплывают.