Better way to maintain this reference on event listener functions

Andrea Giammarchi andrea.giammarchi at gmail.com
Mon May 9 16:43:28 UTC 2016


Gotcha ... so, you've a JS class coupled with the DOM so, since you use
IDs, all you need to eventually do is

```js
onbutton1click(e) {
  // ...
}
onbutton2click(e) {
  // ...
}
onbutton3click(e) {
  // ...
}
handleEvent(e) {
  this['on' + e.curentTarget.id + 'click'](e);
}
```

I believe you would end up with similar code anyway because without
`handleEvent` you would need three different methods anyway.

Once again, my examples were answering your first question that was: I'd
like to set handlers without needing to store the bound reference each time.

Hence all my answers, but I guess it's a matter of personal taste.

Anyway, if it has to be native and different from `handleEvent`, the
`el.addEventListener('click',
this.onClick, {context: this});` variant would be my best pick.

Best Regards




On Mon, May 9, 2016 at 5:37 PM, Mark Kennedy <mkay581 at gmail.com> wrote:

> 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/410022af/attachment-0001.html>


More information about the es-discuss mailing list