Passive event listeners are particularly important: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Just for my own curiosity, why do you think this is something lit-extended should enforce?
This isn't about enforcing, just exposing options. Passive event listeners are important on scroll and wheel events for reducing jank, so it'd be nice if declarative event listeners could use the passive option.
Neat!
certain events should be passive by default e.g. touch and pointer events.
Consider the following:
_render({currentTime}) {
return html`
<tracked-el
on-touchstart="${e => this.onTouchstart(e)}"
></tracked-el>`
}
connectedCallback() {
requestAnimationFrame(() => {
this.currentTime = video.currentTime
})
}
This will cause chrome to log several thousand warnings to the console within a few seconds.
For anyone interested this is my current workaround for such cases:
_render({currentTime}) {
return html`
<tracked-el></tracked-el>`
}
connectedCallback() {
requestAnimationFrame(() => {
this.currentTime = video.currentTime
})
}
_firstRendered() {
this.shadowRoot.querySelector('tracked-el')
.addEventListener('touchstart', e => this.onTouchstart(e), { passive: true })
}
I can think of a few syntactic options for passing options to lit:
Pass an object with handleEvent and options properties:
html`<div on-scroll=${{handleEvent: (e) => doSomething(e), options: {passive: true}}}></div>`;
Pros: Nicely explicit. Familiar by reusing an object with a handleEvent as accepted by addEventListener
Cons: Verbose. Slight chance of a breaking change if objects with handleEvent and options properties are already passed to lit.
Pass an array:
html`<div on-scroll=${[(e) => doSomething(e), {passive: true}]}></div>`;
Pros: Concise. Kind of like the argument list to addEventListener.
Cons: Not obvious? Why is an array passed when a function is expected?
cc @sorvell @kevinpschaaf @azakus
And while those are both ok syntaxes, they both those suffer from a problem a creating fresh objects each render and forcing lit-extended to deep compare the options object.
We'll probably have to recommend a pattern of storing the configuration outside of the template, like:
class MyElement extends LitElement {
_scrollListener = {
handleEvent: (e) => this.scroll(e),
options: {passive: true},
};
_render() {
return html`<div on-scroll=${this._scrollListener}></div>`;
}
}
In this case I think the object form is much easier to read.
Another option would be to pass these options in the attribute name. Possible syntaxes:
html`<div on-scroll:passive=${(e) . => doSomething(e)}></div>`;
html`<div on-scroll.passive=${(e) . => doSomething(e)}></div>`;
// These throw errors when adding them using setAttribute, but they parse fine ¯\_(ツ)_/¯
html`<div on-scroll|passive=${(e) . => doSomething(e)}></div>`;
html`<div on-scroll(passive)=${(e) . => doSomething(e)}></div>`;
__Pros__:
__Cons__:
on-scroll:passive a consumer can never use a custom event called <whatever>:passive)Ping! Any updates?
Most helpful comment
Another option would be to pass these options in the attribute name. Possible syntaxes:
__Pros__:
__Cons__:
on-scroll:passivea consumer can never use a custom event called<whatever>:passive)