- From: Jonas Sicking <jonas@sicking.cc>
- Date: Thu, 10 Jan 2013 03:35:26 +0100
- To: Anne van Kesteren <annevk@annevk.nl>
- Cc: Yehuda Katz <wycats@gmail.com>, DOM public list <www-dom@w3.org>
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 02:36:23 UTC