Lit-html: Support options for declarative event listeners in lit-extended

Created on 21 Oct 2017  Â·  8Comments  Â·  Source: Polymer/lit-html

Passive event listeners are particularly important: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

Low Enhancement

Most helpful comment

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__:

  • declarative
  • no issues with object/array
  • options cannot change, which is a good thing: if the options change we need to remove the listener with the old options and add it with the new options

__Cons__:

  • it limits the event names consumers can use (e.g. if we go for on-scroll:passive a consumer can never use a custom event called <whatever>:passive)
  • only works for boolean flags, so will this work forever?
  • complicates parsing of event listener attributes

All 8 comments

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:

  1. 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.

  2. 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__:

  • declarative
  • no issues with object/array
  • options cannot change, which is a good thing: if the options change we need to remove the listener with the old options and add it with the new options

__Cons__:

  • it limits the event names consumers can use (e.g. if we go for on-scroll:passive a consumer can never use a custom event called <whatever>:passive)
  • only works for boolean flags, so will this work forever?
  • complicates parsing of event listener attributes

Ping! Any updates?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

valdrinkoshi picture valdrinkoshi  Â·  5Comments

dakom picture dakom  Â·  4Comments

AndyOGo picture AndyOGo  Â·  3Comments

pjtatlow picture pjtatlow  Â·  3Comments

depeele picture depeele  Â·  3Comments