Better way to maintain this reference on event listener functions

Mark Kennedy mkay581 at gmail.com
Mon May 9 16:37:02 UTC 2016


Sorry maybe I'm not explaining this the right way. My original intent is to
lessen the code that I would have to write to achieve multiple event
handlers which do different things on multiple DOM elements when
constructing a class and also REMOVING the event listeners manually. I am
assuming this is a scenario where the DOM elements are outliving their
event listeners (for single page web applications for instance) so they
must be removed manually. Let's say I have five buttons on a page and I
want them all to do something different when they are clicked.

```html
<button id="button1">button 1</button>
<button id="button2">button 2</button>
<button id="button3">button 3</button>
```

With your approach, I would have to do this, right?

```js
class SomeClass {
  constructor() {
    let button1 = document.getElementById('button1');
    button1.addEventListener('click', this);
    let button2 = document.getElementById('button2');
    button2.addEventListener('click', this);
    let button3 = document.getElementById('button3');
    button3.addEventListener('mouseover', this);
  },

  onclick(e) {
   // if e.target is button1,
          // show modal
   // if e.target is button2,
          // navigate backwards
   // i f e.target is button 3,
          // do something else
  },

    onmouseover (e) {
      // if e.target is button 3
          // do something else different from the above
    }

  handleEvent(e) {
    this['on' + e.type](e);
}
```
It may just be me confused here, but the above code is much more
overwhelming and much less intuitive than the solutions I've proposed above.

Hopefully this helps clarify a few things.


On Mon, May 9, 2016 at 12:20 PM Andrea Giammarchi <
andrea.giammarchi at gmail.com> wrote:

> Hi Mark, not sure I understand. What you are doing is exactly the same,
> there is no difference whatsoever with what I've shown to you.
>
> > Yes but what happens when you have multiple event targets using the
> same `type` of event? You're going to require a lot of extra conditionals
> in `onclick` method. Again this approach is cumbersome and, imo, not the
> most efficient.
>
> conditionals what? the onclick is called only when the node where you
> attached the listener as instance gets invoked. It never triggers in any
> other cases.
> Or better, it is exactly the same as `someNode.addEventListener('click',
> (e) => this.onClick(e))` ... really, PRECISELY the same.
>
> Whenever your `click` gets triggered, the `handleEvent` would behave
> **exactly** the same. Not sure I've stressed the *exactly* part enough so
> nothing is cumbersome, maybe you don't understand or you've never used this
> approach before.
>
> Otherwise, please show me a single example where adding a listener as
> callback, or bound callback, would be triggered differently from adding an
> instance with an inherited, or own, handleEvent.
>
> Going on ...
>
> > 1. When an arrow function is used as the second parameter to
> `addEventListener`, the language can evaluate and use its scoped context
>  ...
>
> This is not going to happen. It's an exception to the arrow that would
> confuse even more about its context.
>
> > 2. Another solution would be if we could maybe pass a method string as
> the second parameter and then an optional context as the fourth parameter
> ...
>
> This is DOM land, since it's about `addEventListener` signature, and I
> would personally vote -1 to that variant.
>
>
> You should really try to understand how `handleEvent` works, IMO. It's the
> sugar you're looking for already since it makes you set any listener you
> want *without* needing to store upfront the bound method: fast, clean,
> simple.
>
> To remove a listener at any time, you don't need to store upfront a bound
> version of the method.
>
> Best Regards
>
>
>
>
> On Mon, May 9, 2016 at 5:05 PM, Mark Kennedy <mkay581 at gmail.com> wrote:
>
>> Yes but what happens when you have multiple event targets using the same
>> `type` of event? You're going to require a lot of extra conditionals in
>> `onclick` method. Again this approach is cumbersome and, imo, not the most
>> efficient.
>>
>> These may not be the best solutions but here are a few options I've
>> thought of:
>>
>> 1. When an arrow function is used as the second parameter to
>> `addEventListener`, the language can evaluate and use its scoped context
>> when the same function is used with a subsequent `removeEventListener call,
>> so essentially the following code would work when calling destroy.
>>
>>
>> ```js
>> class MyClass {
>> constructor () {
>> someNode.addEventListener('click', (e) => this.onClick(e))
>> }
>>
>> onClick (e) {
>> // do something here
>> }
>>
>> destroy () {
>> someNode.removeEventListener('click', (e) => this.onClick(e))
>> }
>> }
>>
>> ```
>>
>> 2. Another solution would be if we could maybe pass a method string as
>> the second parameter and then an optional context as the fourth parameter
>> to addEventListener and removeEventListener as follows:
>>
>>
>> ```js
>> class MyClass {
>> constructor () {
>> someNode.addEventListener('click', 'onClick', {}, this)
>> }
>>
>> onClick (e) {
>> // do something here
>> }
>>
>> destroy () {
>> someNode.removeEventListener('click', 'onClick', {}, this)
>> }
>> }
>>
>> ```
>>
>>
>> On Mon, May 9, 2016 at 9:52 AM Andrea Giammarchi <
>> andrea.giammarchi at gmail.com> wrote:
>>
>>> uhm, I've used commas ... anyway, the sugar is desugaring to old
>>> methods, this is working example:
>>>
>>> ```js
>>> class SomeClass {
>>>   constructor(someNode) {
>>>     someNode.addEventListener('click', this);
>>>   }
>>>   onclick(e) {
>>>     alert(this.constructor.name); // SomeClass
>>>   }
>>>   handleEvent(e) {
>>>     this['on' + e.type](e);
>>>   }
>>> }
>>>
>>> new SomeClass(document.documentElement);
>>> ```
>>>
>>> The difference with your example is that you will always be able to
>>> remove the instance without needing to store upfront every bound listener.
>>>
>>> To know where the `addEventListener` was set you always have the
>>> `e.currentTarget` so basically you have a weakmap between a node and an
>>> object where you can always retrieve the initial node that used the object
>>> through the event, keeping the node clean from "expando" links.
>>>
>>> More sugar than this, I'm not sure what would be.
>>>
>>> You could also have a simple naming convention where every method that
>>> starts with `on` will be set as listener using the current instance, and do
>>> the same, if necessary, on teardown/destroy.
>>>
>>> What kind of sugar would you use otherwise?
>>>
>>> The only proposal discussed so far is `el.addEventListener('click', ::
>>> this.onClick)`, unfortunately that doesn't solve anything because AFAIK
>>> they don't see any advantage in having `::this.onClick === ::
>>> this.onClick` which is what I've raised already as "that's what
>>> developers would expect" but apparently it's too costy or complicated or
>>> ... dunno.
>>>
>>> ¯\_(ツ)_/¯
>>>
>>> Best Regards
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> On Mon, May 9, 2016 at 2:41 PM, Andrea Giammarchi <
>>> andrea.giammarchi at gmail.com> wrote:
>>>
>>>> Raising a concern about `addEventListener` and its context probably
>>>> made me think it was rather a WHATWG concern.
>>>>
>>>> Anyway, I don't know why you had to point out the `class` keyword ... I
>>>> mean ....
>>>>
>>>> ```js
>>>> class SomeClass {
>>>>   constructor(someNode) {
>>>>     someNode.addEventListener('click', this);
>>>>   },
>>>>   onclick(e) {
>>>>     alert(this.constructor.name); // SomeClass
>>>>   },
>>>>   handleEvent(e) {
>>>>     this['on' + e.type](e);
>>>>   }
>>>> }
>>>> ```
>>>>
>>>> There you go
>>>>
>>>> Best Regards
>>>>
>>>>
>>>> On Mon, May 9, 2016 at 2:38 PM, Mark Kennedy <mkay581 at gmail.com> wrote:
>>>>
>>>>> Wow that's so ironic because [I posted this same idea](
>>>>> https://github.com/whatwg/dom/issues/245#issuecomment-217816301)
>>>>> (literally
>>>>> copied and pasted) in WHATWG's "DOM land" and they told me this was an
>>>>> es-discuss issue. So which is it?
>>>>> Oh and thanks for the code sample but it uses the old prototypical
>>>>> method
>>>>> of replicating a class by creating a function which is now not the most
>>>>> efficient way (there's the `class` keyword). And I don't see how what
>>>>> you're doing isn't any different from a roundabout way of what I did. I
>>>>> already know about the `handleEvent()` stuff, I like that it's
>>>>> available
>>>>> and its polyfills. They are great, but my original question is to
>>>>> implement
>>>>> sugar so that you don't have to use polyfills or the `handleEvent()`.
>>>>> --
>>>>>
>>>>> mark
>>>>>
>>>>> Sent while riding a hoverboard...
>>>>> heyimmark.com :)
>>>>>
>>>>> _______________________________________________
>>>>> es-discuss mailing list
>>>>> es-discuss at mozilla.org
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>
>>>>>
>>>>
>>> --
>>
>> mark
>>
>> Sent while riding a hoverboard...
>> heyimmark.com :)
>>
>
> --

mark

Sent while riding a hoverboard...
heyimmark.com :)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160509/76b504bc/attachment-0001.html>


More information about the es-discuss mailing list