|
| 1 | +--- |
| 2 | +layout: guide |
| 3 | +title: Events |
| 4 | +slug: events |
| 5 | +--- |
| 6 | + |
| 7 | +{::options toc_levels="1..3" /} |
| 8 | +* ToC |
| 9 | +{:toc} |
| 10 | + |
| 11 | +## Overview |
| 12 | + |
| 13 | +### Where to add your event listeners |
| 14 | + |
| 15 | +You need to add event listeners in a method that is guaranteed to fire before the event occurs. However, for optimal loading performance, you should add your event listener as late as possible. |
| 16 | + |
| 17 | +You can add event listeners: |
| 18 | + |
| 19 | +* **Via your component's template.** |
| 20 | + |
| 21 | + You can use lit-html `@event` bindings in your template inside the `render` function to add event listeners to your component. |
| 22 | + |
| 23 | + **Example** |
| 24 | + |
| 25 | + ```js |
| 26 | + render() { |
| 27 | + return html`<button @click="${this.handleClick}">`; |
| 28 | + } |
| 29 | + ``` |
| 30 | + |
| 31 | +* **In the component constructor.** |
| 32 | + |
| 33 | + If you need to listen for an event that might occur before your component has been added to DOM, you might need to add the event listener in your component's constructor. |
| 34 | + |
| 35 | + **Example** |
| 36 | + |
| 37 | + ```js |
| 38 | + constructor() { |
| 39 | + super(); |
| 40 | + this.addEventListener('DOMContentLoaded', this.handleLoaded); |
| 41 | + } |
| 42 | + ``` |
| 43 | + |
| 44 | +* **In `firstUpdated`**. |
| 45 | + |
| 46 | + `firstUpdated` is a LitElement lifecycle callback. `firstUpdated` fires after the first time your component has been updated and rendered. See [firstUpdated](/guide/lifecycle#firstupdated) in the Lifecycle documentation for more information. |
| 47 | + |
| 48 | + If the event you're handling can't occur before your component has been updated and rendered for the first time, it's safe and efficient to add a listener for it in `firstUpdated`. |
| 49 | +
|
| 50 | + **Example** |
| 51 | +
|
| 52 | + ```js |
| 53 | + firstUpdated(changedProperties) { |
| 54 | + this.addEventListener('click', this.handleClick); |
| 55 | + } |
| 56 | + ``` |
| 57 | +
|
| 58 | +* **In `connectedCallback`**. |
| 59 | +
|
| 60 | + `connectedCallback` is a lifecycle callback in the custom elements API. `connectedCallback` fires each time a custom element is appended into a document-connected element. See [the MDN documentation on using custom elements lifecycle callbacks](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#Using_the_lifecycle_callbacks) for more information. |
| 61 | +
|
| 62 | + If your component adds an event listener to anything except itself or its children–for example, to `Window`, `Document`, or some element in the main DOM–you should add the listener in `connectedCallback` and remove it in `disconnectedCallback`. |
| 63 | + |
| 64 | + * Removing the event listener in `disconnectedCallback` ensures that any memory allocated by your component will be cleaned up when your component is destroyed or disconnected from the page. |
| 65 | +
|
| 66 | + * Adding the event listener in `connectedCallback` (instead of, for example, the constructor or `firstRendered`) ensures that your component will re-create its event listener if it is disconnected and subsequently reconnected to DOM. |
| 67 | + |
| 68 | + **Example** |
| 69 | + |
| 70 | + ```js |
| 71 | + connectedCallback() { |
| 72 | + super.connectedCallback(); |
| 73 | + document.addEventListener('readystatechange', this.handleChange); |
| 74 | + } |
| 75 | + disconnectedCallback() { |
| 76 | + document.removeEventListener('readystatechange', this.handleChange); |
| 77 | + super.disconnectedCallback(); |
| 78 | + } |
| 79 | + ``` |
| 80 | +
|
| 81 | +### Using `this` in event handlers |
| 82 | +
|
| 83 | +The default JavaScript context object (`this`) inside an event handler belonging to a LitElement-based component is the component itself. |
| 84 | +
|
| 85 | +Therefore, you can use `this` to refer to your element instance inside any event handler: |
| 86 | +
|
| 87 | +```js |
| 88 | +class MyElement extends LitElement { |
| 89 | + render() { |
| 90 | + return html`<button @click="${this.handleClick}">click</button>`; |
| 91 | + } |
| 92 | + handleClick(e) { |
| 93 | + console.log(this.prop); |
| 94 | + } |
| 95 | +} |
| 96 | +``` |
| 97 | +
|
| 98 | +See the [documentation for `this` on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) for more information. |
| 99 | +
|
| 100 | +### Use cases |
| 101 | +
|
| 102 | +* [Fire a custom event from a LitElement-based component](#fire-custom-event). |
| 103 | +* [Handle a custom event fired by a LitElement-based component](#handle-custom-event). |
| 104 | +* [Handle an event fired by a shadow DOM child of your component](#handle-shadow-dom-event). |
| 105 | +* [Add event listeners imperatively](#imperative). |
| 106 | +
|
| 107 | +### Fire an event from a LitElement-based component {#fire-event} |
| 108 | +
|
| 109 | +Fire a custom event: |
| 110 | +
|
| 111 | +```js |
| 112 | +class MyElement extends LitElement { |
| 113 | + render() { |
| 114 | + return html`<div>Hello World</div>`; |
| 115 | + } |
| 116 | + firstUpdated(changedProperties) { |
| 117 | + let event = new CustomEvent('my-event', { |
| 118 | + detail: { |
| 119 | + message: 'Something important happened' |
| 120 | + } |
| 121 | + }); |
| 122 | + this.dispatchEvent(event); |
| 123 | + } |
| 124 | +} |
| 125 | +``` |
| 126 | +
|
| 127 | +Fire a standard event: |
| 128 | +
|
| 129 | +```js |
| 130 | +class MyElement extends LitElement { |
| 131 | + render() { |
| 132 | + return html`<div>Hello World</div>`; |
| 133 | + } |
| 134 | + updated(changedProperties) { |
| 135 | + let click = new Event('click'); |
| 136 | + this.dispatchEvent(click); |
| 137 | + } |
| 138 | +} |
| 139 | +``` |
| 140 | +
|
| 141 | +### Handle an event fired by a LitElement-based component {#handle-fired-event} |
| 142 | +
|
| 143 | +Handle events fired by a LitElement-based component the same way you would handle any event fired from a standard DOM element. |
| 144 | +
|
| 145 | +To handle an event fired by a LitElement-based component that you're using on any HTML page: |
| 146 | + |
| 147 | +```html |
| 148 | +<my-element onMyEvent="(e) => {console.log(e)}"></my-element> |
| 149 | +<my-element onClick="(e) => {console.log(e)}"></my-element> |
| 150 | +``` |
| 151 | + |
| 152 | +To handle a custom event fired by a LitElement-based component from inside another LitElement template: |
| 153 | + |
| 154 | +```html |
| 155 | +<my-element @my-event="${() => { console.log(event.detail.message) }}"></my-element> |
| 156 | +``` |
| 157 | + |
| 158 | +## Working with events and shadow DOM |
| 159 | + |
| 160 | +When working with events and shadow DOM, there are a few things you need to know about. |
| 161 | + |
| 162 | +### Event bubbling |
| 163 | + |
| 164 | +Some events bubble up through the DOM tree, so that they are detectable by any element on the page. |
| 165 | + |
| 166 | +Whether or not an event bubbles depends on the value of its `bubbles` property. To check if a particular event bubbles: |
| 167 | + |
| 168 | +```js |
| 169 | +handleEvent(e){ |
| 170 | + console.log(e.bubbles); |
| 171 | +} |
| 172 | +``` |
| 173 | + |
| 174 | +See the MDN documentation on the [Event interface](https://developer.mozilla.org/en-US/docs/Web/API/Event) for more information. |
| 175 | + |
| 176 | +### Event retargeting |
| 177 | + |
| 178 | +Bubbling events fired from within shadow DOM are retargeted so that, to any listener external to your component, they appear to come from your component itself. |
| 179 | + |
| 180 | +**Example** |
| 181 | + |
| 182 | +```html |
| 183 | +<my-element onClick="(e) => console.log(e.target)"></my-element> |
| 184 | +``` |
| 185 | + |
| 186 | +```js |
| 187 | +render() { |
| 188 | + return html` |
| 189 | + <button id="mybutton" @click="${(e) => console.log(e.target)}"> |
| 190 | + click me |
| 191 | + </button>`; |
| 192 | +} |
| 193 | +``` |
| 194 | + |
| 195 | +When handling such an event, you can find where it originated from with `composedPath`: |
| 196 | + |
| 197 | +```js |
| 198 | +handleMyEvent(event) { |
| 199 | + console.log('Origin: ', event.composedPath()[0]); |
| 200 | +} |
| 201 | +``` |
| 202 | + |
| 203 | +### Custom events |
| 204 | + |
| 205 | +By default, a bubbling custom event fired inside shadow DOM will stop bubbling when it reaches the shadow root. |
| 206 | + |
| 207 | +To make a custom event pass through shadow DOM boundaries, you must set both the `composed` and `bubbles` flags to `true`: |
| 208 | + |
| 209 | +```js |
| 210 | +firstUpdated(changedProperties) { |
| 211 | + let myEvent = new CustomEvent('my-event', { |
| 212 | + detail: { message: 'my-event happened.' }, |
| 213 | + bubbles: true, |
| 214 | + composed: true }); |
| 215 | + this.dispatchEvent(myEvent); |
| 216 | +} |
| 217 | +``` |
| 218 | + |
| 219 | +See the [MDN documentation on custom events](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) for more information. |
0 commit comments