W3C home > Mailing lists > Public > www-dom@w3.org > January to March 2013

Re: Better event listeners

From: Jake Verbaten <raynos2@gmail.com>
Date: Wed, 9 Jan 2013 19:53:22 -0800
Message-ID: <CAMCMjp2BRu-fsS8px_igfezQ54GyPY+_vAeKQh_pN8gp6U7Qhw@mail.gmail.com>
To: Jonas Sicking <jonas@sicking.cc>
Cc: Anne van Kesteren <annevk@annevk.nl>, Yehuda Katz <wycats@gmail.com>, DOM public list <www-dom@w3.org>
that element.on(new MyClass) sounds like the kind of thing you would find
an opinionated OOP MVC framework.

It's probably going to unsuitable to add something like that to the DOM as
those opinionated frameworks change their mind about "what is best" every
year.

also note that

var c = bindAll(MyClass())
target.on("click", c.onclick)
...

solves your problem.


On Wed, Jan 9, 2013 at 6:35 PM, Jonas Sicking <jonas@sicking.cc> wrote:

> On Jan 5, 2013 4:28 AM, "Anne van Kesteren" <annevk@annevk.nl> wrote:
> >
> > We discussed this a long time ago. Lets try again. I think ideally we
> > introduce something like http://api.jquery.com/on/ Lets not focus on
> > the API for now, but on what we want to accomplish.
> >
> > Type and callback are the basics. Selector-based filtering of
> > event.currentTarget should be there, but optional. I'm not sure about
> > the data feature, Yehuda?
>
> What I understand many people to do is to attach an event listener at
> the root of a subtree (often the document) and then filter based on if
> the clicked (or whatever the event represents) matches a given
> selector. The effect of that is similar to if you had attached an
> event listener to all elements matching a given selector, and keep
> registering and unregistering as elements are added and removed.
>
> In order for that to work you need to do selector-based filtering of
> event.target, not event.currentTarget.
>
> > Should we make all events bubble for the purposes of this new
> > registration mechanism? I actually thought Jonas said jQuery did that
> > at some point, but the jQuery documentation does not suggest it does.
>
> The pattern described above only works on events that bubble. I think
> we've received pressure every now and then from authors to make
> certain events bubble just so that they can use that pattern. Note
> that this is also useful if you aren't doing selector-based matching,
> but rather want to be notified any time some particular event happens.
>
> In general I think the original DOM specs got it right when they
> created the capture, target and bubble phases. And when they realized
> that for some events you only want to listen to the target, and for
> some you want to listen both on target and on ancestors.
>
> However I think they used the wrong solution by making the distinction
> solely based on what the type of the event is. While the type of the
> event certainly is a good indicator for if an event handler is most
> likely going to want to listen only to events fired at a particular
> node, or on all events fired on a subtree, there are exceptions.
>
> Attaching event handlers in a sub tree and then doing filtering based
> on selectors or node names to only catch events fired at certain
> targets, seems like a good example of such an exception.
>
> It has been suggested (both here and elsewhere, including by me) that
> you can use capturing event listeners to implement a
> catch-all-in-subtree listener. However I've been convinced that this
> isn't a good solution.
>
> Capturing event handlers were created to permit a generic top-level
> handlers which override event handlers on descendants, typically on
> the target itself. By using .preventDefault() and .defaultPrevented
> the capturing event handler can signal to event handlers on the target
> that it already has handled the event and that no further action
> should be taken.
>
> Bubbling event handlers allow the opposite. I.e. a generic top-level
> handler which only take action if event handlers on descendants hasn't
> already handled the event.
>
> By telling people that they have to use capturing handlers we make
> this impossible. Generic handlers on ancestors would always execute
> before more specific handlers on the target.
>
> So I don't think that we want to force people to use capturing
> listeners any time they want to catch all events targeted at a
> particular subtree.
>
> Instead we should make it possible to at the time of registration,
> select whether the listener wants to listen to during the bubbling,
> target or capture phase. And maybe allow multiple phases. We could
> certainly have defaults based on the event type, but I think it should
> be possible to override that default.
>
>
> On the subject of things that we want to accomplish with this new API:
>
> One thing not mentioned in your original email which I *think* might
> be nice to accomplish would be to allow more OOP-style use of events,
> but with JS flavor. In particular, what a lot of people do right now
> is to write code like:
>
> foo.addEventListener("click", myHandlerFunction);
> function myHandlerFunction(event) { ... };
>
> Or
>
> foo.addEventListener("click", function(event) { ... });
>
> In other words, they pass a function as the event handler.
>
> Unfortunately javascript doesn't let you do
>
> foo.addEventListener("click", object.clickhandler);
>
> If you do that, the "this" object when clickhandler is called won't be
> |object| but rather |foo|. Instead people end up doing
>
> foo.addEventListener("click", object.clickhandler.bind(object));
>
> which is pretty verbose.
>
> It would be nice if it was possible to automatically register a set of
> functions on an object to as listener for a set of events. And that
> the functions were dispatched such that the 'this' object was the
> "correct" object. In other words, that you could actually use OOP
> style for your code. Especially since a lot of large JS codebases use
> OOP style to a large extent. Which shouldn't be surprising given that
> JS is, you know, a OOP based language :)
>
> There are many ways to accomplish this, and I don't want to make a
> specific proposal here since this thread is about gathering
> requirements not proposals. But I also don't want people to dismiss
> this idea just because they don't like specific ways to accomplish
> this.
>
> So here are a couple of potential solutions:
>
> function MyClass(state) {
>   this._state = state;
> }
> MyClass.prototype = {
>   onclick: function(event) { ... },
>   ondblclick: function(event) { ... },
>   somehelperfunction: function(x) { ... }
> }
> element.on(["click", "dblclick"], new MyClass(1234));
>
>
> or
>
>
> function MyClass(state) {
>   this._state = state;
> }
> MyClass.prototype = {
>   onclick: function(event) { ... },
>   ondblclick: function(event) { ... },
>   somehelperfunction: function(x) { ... }
> }
> element.on(new MyClass(1234));
> // we enumerate all properties of the object and add event listener
> based on property names starting with "on".
>
>
> or
>
>
> function MyClass(state) {
>   this._state = state;
> }
> MyClass.prototype = {
>   onclick: function(event) { ... },
>   myDoubleClickHandler: function(event) { ... },
>   somehelperfunction: function(x) { ... }
> }
> element.on({ click: "onclick", dblclick: "myDoubleClickHandler" }, new
> MyClass(1234));
>
>
> / Jonas
>
>
Received on Thursday, 10 January 2013 03:53:50 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Thursday, 10 January 2013 03:53:53 GMT