Это пост №67 из серии, посвященный изучению JavaScript и его строительных компонентов. В процессе определения и описания основных элементов мы также поделимся некоторыми практическими правилами, которые мы используем при создании SessionStack, инструмента JavaScript для разработчиков, позволяющего идентифицировать, визуализировать и воспроизводить ошибки веб-приложений посредством воспроизведения сеанса с точностью до пикселя.

Введение

События — это сигналы или действия, запускаемые или испускаемые системами, с которыми мы взаимодействуем программно. И эти сигналы улавливаются и обрабатываются прослушивателями событий — разделами кода, которые прослушивают события и реагируют на них.

События генерируются для уведомления наших программ об изменениях, которые могут повлиять на выполнение. И эти изменения могут быть связаны с действиями пользователя, такими как движения мыши, нажатия кнопок и т. д.

В JavaScript сгенерированное событие представлено объектом события, который содержит методы и свойства или информацию о событии. И этот объект события передается в качестве аргумента любому обработчику событий, прослушивающему событие.

Вот некоторые из свойств и методов объекта события:

  • isTrusted
  • bubbles
  • cancelBubble
  • currentTarget
  • defaultPrevented
  • srcElement
  • target
  • timeStamp
  • type
  • stopPropagation
  • preventDefault
  • stopImmedaitePropagation
  • initEvent

В JavaScript существуют разные типы событий, и событие может возникать в результате взаимодействия с пользователем или программно из нашего кода.

Термин интерфейс события относится к событиям, происходящим в DOM, и эти события имеют имя, оканчивающееся на Event. Примеры таких событий:

Генерация и обработка событий

В JavaScript события DOM, генерируемые программно, называются синтетическими событиями.

И в этом разделе мы узнаем, как программно создавать и обрабатывать события.

Как создавать события

События создаются путем вызова конструктора Event, как показано ниже:

Приведенный выше код создает событие greet и возвращает объект события — с type: “greet”. Но этот объект события greet сам по себе ничего не делает. Нам нужно добавить некоторый код, который прослушивает эмиссию этого события и отвечает на него.

Как обрабатывать события

В JavaScript мы прослушиваем события с помощью прослушивателей событий или обработчиков событий.

Таким образом, чтобы добавить прослушиватель к событию greet выше, мы будем использовать метод addEventListener, как показано ниже:

В приведенном выше коде мы вызвали метод addEventListener для файла eventTarget. Здесь eventTarget относится к объекту, например, к объекту Document или window, а также к любому родительскому или дочернему элементу, который может получать события и иметь для них прослушиватели.

Мы узнаем больше об этом позже. Таким образом, приведенный выше код прослушивает генерацию события greet в файле eventTarget.

Однако нам по-прежнему необходимо уведомлять всех слушателей, когда это событие запускается. И для этого мы используем dispatchEvent(event), как показано ниже:

eventTarget.dispatchEvent(event);

Метод dispatchEvent(event) принимает объект события в качестве аргумента и отправляет его. И как только это событие сгенерировано, dispatchEvent(event) синхронно вызывает все связанные слушатели, тем самым вызывая ответ.

Наш окончательный код должен выглядеть так:

Чтобы уточнить это, мы будем использовать реальные элементы в качестве eventTarget, как показано ниже:

Приведенный выше код аналогичен нашему предыдущему примеру. Но здесь мы использовали метод querySelector, чтобы получить ссылку на eventTarget — элемент DOM с id=”greet”. И мы сохранили эту ссылку в переменной elem, используемой во всем коде. Результат такой же, как и в предыдущем примере, с небольшим дополнением, представленным строкой:

elem.innerHTML = “Greetings from John Doe!”

И это печатает “Greetings from John Doe!” в браузере, когда событие срабатывает. Вы можете увидеть это в действии здесь.

Обратите внимание, что термины «слушатели событий» и «обработчики событий» вольно используются для обозначения одного и того же. Но в строгом смысле прослушиватели событий относятся к коду, используемому для добавления прослушивателя к цели события:

eventTarget.addEventListener(‘click’, function() { /* do stuff here*/ }, false);

В то время как обработчик событий относится к функции обработчика, вызываемой после того, как слушатель уведомлен об испускаемом событии. После этого несколько прослушивателей событий могут использовать одни и те же обработчики, как показано ниже:

В приведенном выше коде мы создали обработчик событий — handleLogDetails и использовали его во всех прослушивателях событий. Кроме того, мы использовали конструктор CustomEvent, который позволяет нам создавать события с дополнительной информацией. И эти дополнительные фрагменты информации записываются в консоль.

Вы можете увидеть это в действии здесь.

В дополнение к добавлению прослушивателей событий к целям событий мы также можем программно удалить прослушиватели событий, используя метод removeEventListener, как показано ниже:

Делегация мероприятия

До сих пор мы узнали о добавлении прослушивателей событий к одному элементу. Но что произойдет, если мы захотим прослушивать события, исходящие от множества дочерних элементов? Делегирование событий дает нам чистый и эффективный шаблон, который позволяет нам добавить одного слушателя к родительскому элементу. И этот прослушиватель событий будет прослушивать и анализировать всплывающие события для каждого дочернего элемента.

Делегирование событий использует всплывающее окно событий, поэтому, прежде чем мы узнаем, как реализовать делегирование событий, давайте узнаем о всплывающем отображении событий.

Всплывание и захват событий

Всплывающие события, цель и захват — это 3 фазы потока событий в браузере. Они описывают, как браузеры обрабатывают события, запускаемые вложенными элементами.

Во время фазы всплытия событие, инициированное дочерним элементом, всплывает вверх по дереву DOM. И это событие может быть перехвачено и обработано любым прослушивателем событий на его элементе-предке — внешних элементах.

Событие всплывает только в том случае, если логическое свойство bubble равно true.

Во время фазы всплытия браузер начинает искать слушателей из непосредственного родителя дочернего элемента, из которого запускается событие. Затем браузер продолжает поиск вверх по дереву DOM.

Рассмотрим код ниже:

Приведенный выше код демонстрирует всплытие событий. И мы видим, что при нажатии кнопки событие всплывает в DOM. Следовательно, прослушиватели событий всех его элементов-предков уведомляются о событии click и регистрируют свои ответы на консоли.

Вы можете увидеть это в действии здесь.

Фаза захвата обратна фазе пузыря. На этапе захвата браузер начинает поиск слушателей из самого внешнего элемента-предка — элемента html и ищет вниз по дереву DOM, пока не найдет прямого родителя дочернего элемента, вызвавшего событие.

Наконец, на целевой фазе браузер проверяет, есть ли у цели события прослушиватель событий для запущенного события. И после этого он передает событие непосредственному родительскому элементу и продолжает распространять его вверх по дереву DOM, пока не достигнет своего самого внешнего элемента-предка. Обратите внимание, что браузер будет распространять событие вверх по дереву DOM только в том случае, если свойство bubbles созданного события равно true.

Мы можем определить, какая фаза потока событий в настоящее время выполняется программно из свойства eventPhase.

По умолчанию события JavaScript проходят этапы захвата и назначения. Но событие переходит в фазу всплытия только в том случае, если свойство bubbles истинно. Кроме того, все прослушиватели событий по умолчанию регистрируются в фазе всплытия. И если вы хотите зарегистрировать событие для фазы захвата, вы можете установить для необязательного третьего свойства метода addEventListener значение true.

Остановить распространение

Всплывающие события могут быть нежелательным эффектом в некоторых сценариях. И в таких случаях мы можем предотвратить распространение события, вызвав метод stopPropagation для объекта события. Таким образом, вызывая метод stopPropagation в нашем предыдущем примере, мы можем предотвратить всплытие текущего события вверх по дереву DOM. Для этого измените фрагмент кода JavaScript в приведенном выше примере, как показано ниже:

Метод stopPropagation не предотвращает какое-либо поведение по умолчанию, такое как перезагрузка формы после ее отправки, и нажатие на ссылки по-прежнему будет работать. Но чтобы предотвратить поведение по умолчанию, метод preventDefault.

Кроме того, если это событие прослушивается несколькими обработчиками событий, вызов метода stopPropagation не предотвратит распространение события на эти обработчики. Но для этого можно использовать метод preventImmediatePropagation.

Теперь, когда мы понимаем пузырьковую передачу событий, мы можем научиться реализовывать делегирование событий.

Как упоминалось выше, делегирование событий позволяет нам с пользой использовать всплытие событий. Чтобы увидеть это в действии, рассмотрим веб-страницу со следующим HTML:

В приведенном выше коде элемент section имеет пять дочерних элементов. К каждому дочернему элементу добавлен атрибут имени с использованием глобальных атрибутов HTML 5 data-*. Наша задача — регистрировать атрибут name дочернего элемента в консоли при нажатии на этот дочерний элемент.

Таким образом, вместо того, чтобы добавлять прослушиватель событий к каждому дочернему элементу, мы можем использовать делегирование событий и полагаться на пузырьковое распространение событий для распространения события вверх по дереву DOM. При этом нам нужно всего лишь добавить одного слушателя к самому внешнему элементу в дереве DOM, как показано ниже:

В приведенном выше коде мы добавили прослушиватель событий в документ, поэтому любое событие, генерируемое любым элементом дерева DOM, будет перехватываться и обрабатываться нашим прослушивателем.

Таким образом, приведенный выше код выводит на консоль атрибут name дочернего элемента, по которому щелкнули. И вы можете увидеть это в действии здесь.

Из нашей реализации делегирования событий видно, что делегирование событий дает нам чистый и эффективный способ обработки событий, исходящих от вложенных элементов.

Заключение

В этой статье мы узнали о событиях в JavaScript, а также о том, как их создавать и обрабатывать.

Мы также узнали о делегировании событий — паттерне, который позволяет нам с пользой использовать всплытие или распространение событий.

А в случаях, когда распространение события не требуется, мы научились останавливать распространение события.

События JavaScript очень важны для нас в наши дни, потому что они закладывают основу для полезных шаблонов, таких как шаблон наблюдателя и шаблон публикации-подписки.

SessionStack использует pub/sub-сервисы для обработки всех полученных поведенческих данных из браузера в режиме реального времени. По мере поступления данных SessionStack позволяет вам просматривать сеансы пользователей в виде видеороликов, что позволяет вам точно видеть, что происходило во время их путешествия.

Сочетая эту визуальную информацию со всеми техническими данными из браузера, такими как ошибки, трассировка стека, сетевые проблемы, данные отладки и т. д., вы можете легко понять проблемные области в своем продукте и эффективно их решить.

Существует бесплатная пробная версия, если вы хотите попробовать SessionStack.

Хотите узнать больше о JavaScript? Ознакомьтесь со всеми публикациями Как работает JavaScript здесь».