`on()` collides with all sorts of code in the wild.
Lots of libraries and runtimes use on() as a generic event handling mechanism. This name might cause problems for them updating and as such cause unnecessary friction. It would be good to see if there is another name that is less prevalent in the same design space that wouldn't collide with Cloudflare Workers, Bun, Node.js, etc. and the libraries written for them (Note: all of these have some form of EventTarget, but often mix with EventEmitter's on in the wild or for backwards compatibility).
Yup, that was my first reaction as well. I still like the Observable.from from the old proposal
That's very true. I personally don't care if it's on or some other name. button.events('click'), button.when('click'), button.observe('click') or some such thing.
@ming-codes: I'm not sure what you mean by Observable.from, are you saying that EventTarget would somehow implement a symbol or something that it would recognize? Since addEventListener requires a sort of magic string like "click" or "message" or whatever, I'm not sure that's doable. (it's going seeing your name on github again, btw, it's been a minute)
Yes, Observable.from would be like Iterator.from, which response to Symbol.iterator - iow, Symbol.observable would be the protocol that an object uses to indicate it can be observed, and Observable.from would reliably extract that.
are you saying that EventTarget would somehow implement a symbol or something that it would recognize?
Not my I original thought, but I like it. 😄 The Symbol could implement a function that returns an Observable.
[Symbol.observable](eventName) {
...
}
Completely agree. Besides less possibilities of collissions, I think a method like .observe() on the EventTarget instance is much more straightforward and clear.
I'd like to get a better feel for how much collision there might actually be in the wild. Are there huge libraries that supply a .on() method on objects that are currently also EventTargets? It's not clear to me how this would collide with Cloudflare workers, Node.js, etc. as mentioned in the OP.
From looking at Cloudflare workers, they indeed support EventTarget but have nothing to do with EventEmitter (and it's corresponding .on()), so the only surface area for collision here I guess is 3p library code that does what I mentioned above. Node.js implements EventTarget which doesn't provide its own .on(), so I don't see an obvious collision there, but I guess they also have NodeEventTarget which does have .on() (that seems to delegate to the EventEmitter implementation I guess?). That's interesting, but those objects aren't actual EventTargets anyways so maybe it is OK...
Anyways, some concrete examples of this being an issue would be very useful to see!
Yeah, I think in-the-wild data from browser use counters would be necessary to believe there's some sort of unsolvable conflict here.
I would prefer non-browser data be included as well from something like WinterCG or somewhere. Anything providing both EventTarget and EventEmitter interfaces (not inheritance) would be affected.
At some point I can try and look into use counter data for Chromium, but since @bmeck filed this concern initially I'd love if you could take the first leap and help us track down this data from WinterCG or elsewhere, to try and substantiate the concern here.
One slightly interesting conflict I encountered when implementing the EventTarget integration with Observables is this WPT: https://github.com/web-platform-tests/wpt/blob/master/trusted-types/trusted-types-event-handlers.html#L36-L40. Basically this test iterates over everything in the HTMLDivElement prototype and expects anything that begins with on to be an event handler, which it would no longer be given how this proposal is currently shaped.
I have no idea how common this kind of thing is in frameworks or user code. I'd certainly hope it's not common enough to preclude us from using what I think is a nice name like on for this API, but it would be great if any framework authors could weigh in with any insight here.
While I'm all in favor of short names, we have two decades of history where a function on() traditionally took an event type and a callback. Just off the top of my head I can remember:
- Prototype.js:
Element.prototype.on(eventType, callback) - Alpine.js:
x-on:eventType="callback" - Vue.js:
v-on:eventType="callback" - AngularJS:
ng-on-eventType="callback" - React:
onEventType={callback} - htmx:
htmx.on(eventType, callback)andhx-on="eventType: callback" - Unpoly:
up.on(eventType, callback) - jQuery:
$element.on(eventType, callback) - All browser-native event attributes like
onclickoronload.
Although the only hard namespace conflict is Prototype.js, choosing on() will certainly break expectations for how the API works. It will also be somewhat awkward to work with code that uses both traditional on and the new observable API introduced by this proposal.
I hope it is still possible to use a more descriptive name like observe() that comes with less historical baggage.
These are all really good examples, thanks a lot for providing them. They make a strong case for the theoretical problem of name collisions with new/confusing behavior under an old name (on()), however I'd really like to know how much our on() will mess things up, so here's what I'm kind of leaning towards at this point. Chromium has the Origin Trial infrastructure and I think it gives us a good opportunity to trial the Observable API and its EventTarget integration with the on() API, and use that as a concrete way to collect feedback from OT users in the wild, that are using many of the frameworks you listed above.
This should give us a bunch of real-world data as to how suitable this API name is, before we ever launch the API to stable, giving us plenty of flexibility to change things up mid-trial.
Note I was really concerned about breaking expecations, not about actual name collisions. The only name collision from the list above is Prototype.js (which patches Element.prototype). This is a very old framework, and a 2007-era Prototype.js app is unlikely to use this new observable API.
Sorry that I couldn't make this clearer.
No I think that was clear. I tried to capture that in:
with new/confusing behavior under an old name (on())
but could've been clearer myself actually.
Note that an origin trial won’t address or expose user confusion issues, since it simply won’t have anywhere near enough exposure to devs to provide that info.
Hmm, I'm not sure. It's caught lots of things like this in the past. To say that it simply cannot provide a useful signal is to say the entire Origin Trial infrastructure is useless, which seems self-evidently false.
@domfarolino to me it definitely seems like it would provide runtime usage feedback, and expose a subset of devs to it, but that subset won't likely ever be representative of devs as a whole - since aren't origin trials always run on a small number of partner corporate websites?
Check out http://googlechrome.github.io/OriginTrials/developer-guide.html. Any site can opt-in to the Origin Trial. The maximum experiment population size is 0.5% of all page loads across Chrome.
Although the only hard namespace conflict is Prototype.js
FWIW: This shouldn't break Prototype.js apps. Prototype will conflict with and overwrite it, but it won't break.
They're creating a new Element class from the existing global element, then they're adding methods to it by trampling whatever is there (note the lack of the third argument for mergeMethods means it will trample whatever is there).
If we're doing all of this research on the name on, do we have alternative names that we could check at the same time?
Dojo also uses on: https://dojotoolkit.org/reference-guide/1.10/dojo/Evented.html
/cc @dylans
I found that when using Chrome 123 and setting chrome://flags/#observable-api, the Dojo example code-dialog is broken at: https://dojotoolkit.org/reference-guide/1.10/dojo/Evented.html#examples
The example itself runs fine, but when you close the floating "CodeGlass" dialog with the small X in the upper right corner, this error is shown in the console, and you can't reopen the example when you click the Run button again.
If we're doing all of this research on the name on, do we have alternative names that we could check at the same time?
observe() would be right there :slightly_smiling_face:
observe() sounds nice. However I think there was some discussion at TPAC 2023 about making other "observer"-like things — like MutationObserver and IntersectionObserver — into Observables in the fullness of time. I recall @smaug---- mentioning something like this.... maybe. In that case, observe() would be tricky, because of its observe() method.
If we're doing all of this research on the name on, do we have alternative names that we could check at the same time?
How about something more explicit like createObservable?
I think observe() is probably the best, and I think it could probably work even in spite of my prior concerns, since the Observer#observe() method should be able to be disambiguated with MutationObserver#observe() and IntersectionObserver#observe().
since the Observer#observe() method should be able to be disambiguated with MutationObserver#observe() and IntersectionObserver#observe().
They also won't share a common interface as these methods require different arguments. E.g. observing an Element requires an event type, but observing a MutationObserver requires either no arguments (if we're observing all registered elements) or an Element (if we're only observing a single element registration).
MutationObserver.observe() certainly requires at least one argument.
Is there consensus to rename on to observe?
I think I am fine with it, but only if we actually need to change it from on()