События¶
События — это стандартный способ, с помощью которого элементы сообщают об изменениях. Эти изменения обычно происходят из-за взаимодействия с пользователем. Например, кнопка отправляет событие click
, когда пользователь нажимает на нее; элемент input
отправляет событие change
, когда пользователь вводит в него значение.
В дополнение к этим стандартным событиям, которые автоматически отправляются, элементы Lit могут отправлять пользовательские события. Например, элемент меню может отправлять событие, указывающее на изменение выбранного пункта; всплывающий элемент может отправлять событие, когда всплывающее окно открывается или закрывается.
Любой код Javascript, включая сами элементы Lit, может прослушивать события и выполнять действия на их основе. Например, элемент панели инструментов может фильтровать список при выборе пункта меню; элемент входа в систему может обрабатывать вход в систему при нажатии на кнопку входа.
Прослушивание событий¶
В дополнение к стандартному API addEventListener
, Lit предлагает декларативный способ добавления слушателей событий.
Добавление слушателей событий в шаблоне элемента¶
Вы можете использовать выражения @
в своем шаблоне, чтобы добавить слушателей событий в элементы шаблона компонента. Декларативные слушатели событий добавляются при отрисовке шаблона.
Настройка параметров слушателя событий¶
Если вам нужно настроить параметры события, используемые для декларативного слушателя событий (например, passive
или capture
), вы можете указать их в слушателе с помощью декоратора @eventOptions
. Объект, переданный в @eventOptions
, передается в качестве параметра options
в addEventListener
.
1 2 3 4 5 |
|
Использование декораторов
Декораторы — это предложенная функция JavaScript, поэтому для использования декораторов вам потребуется компилятор типа Babel или TypeScript. Подробности см. в разделе Включение декораторов.
Если вы не используете декораторы, вы можете настроить параметры слушателя событий, передав объект в выражение слушателя событий. Объект должен иметь метод handleEvent()
и может включать любые опции, которые обычно указываются в аргументе options
в выражении addEventListener()
.
1 2 3 4 5 6 |
|
Добавление слушателей событий в компонент или его теневой корень¶
Чтобы получать уведомления о событиях, отправляемых от дочерних элементов компонента, а также дочерних элементов, выводимых в теневой DOM через шаблон компонента, вы можете добавить слушателя в сам компонент с помощью стандартного DOM-метода addEventListener
. Подробности см. в EventTarget.addEventListener() на MDN.
Конструктор компонента — хорошее место для добавления слушателей событий в компонент.
1 2 3 4 |
|
Добавление слушателей событий в сам компонент является формой делегирования событий и может быть сделано для сокращения кода или повышения производительности. Подробности см. в разделе Делегирование событий. Обычно при таком делегировании используется свойство target
события для выполнения действий в зависимости от того, какой элемент вызвал событие.
Однако события, запускаемые из теневого DOM компонента, ретаргетируются, когда их слышит слушатель событий компонента. Это означает, что целью события является сам компонент. Дополнительные сведения см. в разделе Работа с событиями в теневом DOM.
Ретаргетинг может помешать делегированию событий, и чтобы избежать этого, слушатели событий могут быть добавлены в сам теневой корень компонента. Поскольку shadowRoot
недоступен в конструкторе
, слушатели событий могут быть добавлены в методе createRenderRoot
следующим образом. Обратите внимание, что важно убедиться в том, что метод createRenderRoot
возвращает корень тени.
Добавление слушателей событий к другим элементам¶
Если ваш компонент добавляет слушателя событий к чему-либо, кроме себя или своего шаблона DOM — например, к Window
, Document
или какому-либо элементу в основном DOM — вы должны добавить слушателя в connectedCallback
и удалить его в disconnectedCallback
.
-
Удаление слушателя событий в
disconnectedCallback
гарантирует, что вся память, выделенная вашим компонентом, будет очищена, когда ваш компонент будет уничтожен или отключен от страницы. -
Добавление слушателя событий в
connectedCallback
(вместо, например, конструктора илиfirstUpdated
) гарантирует, что ваш компонент будет заново создавать свой слушатель событий, если он будет отключен и впоследствии снова подключен к DOM.
1 2 3 4 5 6 7 8 |
|
Дополнительные сведения о connectedCallback
и disconnectedCallback
см. в документации MDN по использованию пользовательских элементов lifecycle callbacks.
Оптимизация для производительности¶
Добавление слушателей событий происходит очень быстро и обычно не вызывает проблем с производительностью. Однако для компонентов, которые используются очень часто и нуждаются в большом количестве слушателей событий, вы можете оптимизировать производительность первого рендеринга, уменьшив количество используемых слушателей с помощью делегирования событий и добавив слушателей асинхронно после рендеринга.
Делегирование событий¶
Делегирование событий позволяет уменьшить количество используемых слушателей событий и, следовательно, повысить производительность. Также иногда удобно централизовать обработку событий, чтобы сократить объем кода. Делегирование событий можно использовать только для обработки событий, которые пузырятся
. Подробности о пузырьках см. в разделе Диспетчеризация событий.
Пузырящиеся события могут быть услышаны на любом элементе-предке в DOM. Вы можете воспользоваться этим преимуществом, добавив единственный слушатель событий на компонент-предок, чтобы получать уведомления о событии пузырения, отправленном любым из его потомков в DOM. Используйте свойство target
события, чтобы предпринять конкретные действия в зависимости от элемента, отправившего событие.
Асинхронное добавление слушателей событий¶
Чтобы добавить слушателя событий после рендеринга, используйте метод firstUpdated
. Это обратный вызов жизненного цикла Lit, который запускается после того, как компонент впервые обновляет и рендерит свой шаблон DOM.
Обратный вызов firstUpdated
срабатывает после первого обновления компонента и вызова его метода render
, но до того, как браузер успел отрисовать.
Дополнительную информацию см. в разделе firstUpdated документации Lifecycle.
Чтобы убедиться, что слушатель будет добавлен после того, как пользователь увидит компонент, вы можете ожидать Promise, который разрешится после того, как браузер закрасит компонент.
1 2 3 4 5 |
|
Понимание this
в слушателях событий¶
Слушатели событий, добавленные с помощью декларативного синтаксиса @
в шаблоне, автоматически привязываются к компоненту.
Поэтому вы можете использовать this
для ссылки на ваш экземпляр компонента в любом декларативном обработчике событий:
1 2 3 4 5 6 7 8 9 10 |
|
При императивном добавлении слушателей с помощью addEventListener
необходимо использовать функцию-стрелку, чтобы this
ссылался на компонент:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Дополнительную информацию см. в документации по this
на MDN.
Прослушивание событий, запускаемых из повторяющихся шаблонов¶
При прослушивании событий на повторяющихся элементах часто удобно использовать делегирование событий, если событие пузырится. Если же событие не пузырится, можно добавить слушателя на повторяющиеся элементы. Вот пример использования обоих методов:
Удаление слушателей событий¶
Передача null
, undefined
или nothing
в выражение @
приведет к удалению любого существующего слушателя.
Диспетчеризация событий¶
Все узлы DOM могут отправлять события с помощью метода dispatchEvent
. Сначала создайте экземпляр события, указав его тип и параметры. Затем передайте его в dispatchEvent
следующим образом:
1 2 3 4 5 |
|
Опция bubbles
позволяет событию распространяться вверх по дереву DOM к предкам отправляющего элемента. Важно установить этот флаг, если вы хотите, чтобы событие могло участвовать в делегировании событий.
Опцию composed
полезно установить, чтобы позволить событию быть отправленным выше теневого DOM-дерева, в котором существует элемент.
Дополнительную информацию см. в разделе Работа с событиями в теневом DOM.
Полное описание диспетчеризации событий см. в EventTarget.dispatchEvent() на MDN.
Когда отправлять событие¶
События следует отправлять в ответ на взаимодействие с пользователем или асинхронные изменения состояния компонента. Как правило, они не должны отправляться в ответ на изменения состояния, сделанные владельцем компонента через API его свойств или атрибутов. Именно так обычно работают нативные элементы веб-платформы.
Например, когда пользователь вводит значение в элемент input
, диспетчеризируется событие change
, но если код устанавливает свойство value
элемента input
, событие change
не диспетчеризируется.
Аналогично, компонент меню должен отправлять событие, когда пользователь выбирает пункт меню, но он не должен отправлять событие, если, например, установлено свойство selectedItem
меню.
Обычно это означает, что компонент должен отправлять событие в ответ на другое событие, к которому он прислушивается.
Отправка событий после обновления элемента¶
Часто событие должно срабатывать только после обновления и рендеринга элемента. Это может быть необходимо, если событие предназначено для передачи информации об изменении состояния рендеринга на основе взаимодействия с пользователем. В этом случае можно дождаться обещания компонента updateComplete
после изменения состояния, но перед отправкой события.
Использование стандартных или пользовательских событий¶
События могут быть отправлены либо путем создания Event
, либо CustomEvent
. Любой из этих способов является разумным. При использовании CustomEvent
любые данные о событии передаются в свойстве detail
события. При использовании Event
можно создать подкласс события и прикрепить к нему пользовательский API.
Подробности о создании событий см. в Event на MDN.
Запуск пользовательского события:¶
1 2 3 4 5 6 |
|
Дополнительные сведения см. в документации MDN по пользовательским событиям.
Вызов стандартного события:¶
1 2 3 4 5 6 7 8 9 10 |
|
Работа с событиями в теневом DOM¶
При использовании теневого DOM есть несколько модификаций стандартной системы событий, которые важно понимать. Теневой DOM существует в первую очередь для того, чтобы обеспечить механизм определения масштаба в DOM, который инкапсулирует детали об этих "теневых" элементах. Как таковые, события в теневом DOM инкапсулируют определенные детали из внешних элементов DOM.
Понимание диспетчеризации составленных событий¶
По умолчанию событие, отправленное внутри теневого корня, не будет видно вне этого теневого корня. Чтобы событие проходило через границы теневого DOM, необходимо установить свойство composed
в true
. Обычно используется пара composed
с bubbles
, чтобы все узлы в дереве DOM могли видеть событие:
1 2 3 4 5 6 7 |
|
Если событие составное
и пузырьковое
, оно может быть получено всеми предками элемента, отправляющего событие, включая предков во внешних теневых корнях. Если событие составное
, но не пузырьковое
, оно может быть получено только на элементе, который отправляет событие, и на главном элементе, содержащем теневой корень.
Обратите внимание, что большинство стандартных событий пользовательского интерфейса, включая все события мыши, касания и клавиатуры, являются одновременно и пузырьковыми, и составными. Дополнительную информацию см. в MDN документации по составленным событиям.
Понимание ретаргетинга событий¶
Composed события, отправленные из теневого корня, ретаргетируются, то есть для любого слушателя элемента, содержащего теневой корень или любого из его предков, они кажутся исходящими от содержащего элемента. Поскольку Lit-компоненты рендерятся в теневые корни, все составленные события, отправленные изнутри Lit-компонента, выглядят так, будто они отправлены самим Lit-компонентом. Свойством target
события является компонент Lit.
1 2 3 |
|
1 2 3 4 5 6 |
|
В сложных случаях, когда требуется определить происхождение события, используйте API event.composedPath()
. Этот метод возвращает массив всех узлов, пройденных диспетчером события, включая узлы в теневых корнях. Поскольку при этом нарушается инкапсуляция, следует позаботиться о том, чтобы не полагаться на детали реализации, которые могут быть раскрыты. К распространенным случаям использования относится определение того, был ли элемент, на котором щелкнули, тегом якоря, для целей маршрутизации на стороне клиента.
1 2 3 |
|
Дополнительные сведения см. в MDN документации по composedPath.
Общение между диспетчером событий и слушателем¶
События существуют в основном для передачи изменений от диспетчера событий к слушателю, но события также можно использовать для передачи информации от слушателя обратно диспетчеру.
Один из способов сделать это — предоставить API для событий, которые слушатели могут использовать для настройки поведения компонента. Например, слушатель может установить свойство detail пользовательского события, которое диспетчерский компонент затем использует для настройки поведения.
Еще один способ взаимодействия между диспетчером и слушателем — метод preventDefault()
. Он может быть вызван, чтобы указать, что стандартное действие события не должно происходить. Когда слушатель вызывает preventDefault()
, свойство события defaultPrevented
становится истинным. Этот флаг может быть использован слушателем для настройки поведения.
Обе эти техники используются в следующем примере: