Open jakearchibald opened 1 month ago
In the specification activation behavior is associated with the event target, not the event. And it's very much tied to click
events as well.
(This reminds me a bit of the "event group" concept in https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Event-groups.)
cc @smaug---- @rniwa
Gecko has a variant of "event group", so called system event group, and listeners in that are called after the normal DOM event dispatch. (And it was also exposed to XBL) But I wouldn't add anything like that to the web, too complicated.
EventTargets could perhaps have some callback for activation behavior, and that could either override the native activation behavior, or other option is to control that using .preventDefault() if the callback gets the event as param. Certain cases like nesting inside an anchor element might complicate things - which all activation behaviors should be triggered?
EventTargets could perhaps have some callback for activation behavior, and that could either override the native activation behavior
Yeah, I like that idea.
or other option is to control that using .preventDefault() if the callback gets the event as param
I don't think that works, because you want the activation behaviour to not-run if the default is prevented.
For input fields and forms, we have "change" and "submit" events as happening after processing a lower level event like click, keydown, keyup, Enter, drag-and-drop, clipboard paste, etc.
What if anchor elements emitted an event like "navigate" or "activate" which wouldn't be cancellable, to happen when performing the action, regardless of how it was triggered and whether it was cancelled?
For input fields and forms, we have "change" and "submit"
"submit" is another problem event here, as it can be cancelled.
@jakearchibald I see. Another example is HTMLElement.click()
on a <summary>
element, in relation to the toggle
event on the parent <details>
element. The toggle
event is non-cancellable and emitted after, during the activation (but before the next frame, I think?).
I suspect we would see people use event.addActivationBehavior()
also on non-submit buttons and JS-only forms, where there is no default behaviour, as a way to wait for the user's JS application to have finished doing something and thus register a "late" or "after" listener. Is that something we want to encourage? Basically a way to express priority or ordering between event handlers.
I see this come up a fair bit in developer code at Wikimedia, where event+timeout is used as a way to plug into something at a point where there is no DOM event or app-provided callback (yet). Even more so in asynchronous code, where the synchronous assumptions by event handlers (apart from eg. in Service Workers), would be insufficient anyway. Because fireEvent
naturally would not wait for the completion of any async addEventListener(async function…)
. In this case we also tend to see, eventually, given a large enough code base, code that nests two setTimeout's in order to implicitly wait for another handler that does one setTimeout and/or is async. It's an arms race.
For simple one-to-one and synchronous use cases, event.addActivationBehavior()
would allow decoupling and coorporation between event handlers which is great. For the more complex cases, it seems event.addActivationBehavior()
might underfit. B would perhaps be coupling and assuming specific things about what A will have done by then. This would be an unstable contract and likely insufficient. It's not uncommon to see B "chase" A (e.g. attach to click, change, submit, and whatever else may trigger the same application behaviour). In such a situation, would we recommend developers instead emit their own non-cancellable DOM event and/or JS callback, to represent the logical application behaviour?
If so, I wonder if the the platform should follow that pattern too? We have this for toggle
and for a few other things like it. I would consider pageshow
, pagehide
, unload
to fit in this category as well. They emit regardless of how something it happens, it just happens. I can't think of a good name for click and submit activations, but perhaps a non-cancellable activateclick
and activatesubmit
event would make sense (better name welcome!). This would behave very similar to your example, except for the callstack which would presumably be after button.click()
has completed in the next tick. Or not? I mean, nested events do exist right? E.g. we have change/blur/focus which afaik involve some nesting where a single behaviour trigger ends up synchronously emitting multiple events. Either way makes sense, but if it's a requirement that it happen as part of element.click()
then perhaps that would be the better choice.
The reason I'm saying all this, is because I think re-using the event system is easier to learn, easier to extend and adopt for applications with their own events, scales to async behaviours, and perhaps also easier to integrate into frameworks and abstractions that already understand events.
I also see value in being able to listen to these through propagation, without needing to attach a synchronous listener to (one or more) possible earlier events that lead to the activation. Think about scroll events and the jank they caused. In other words addActivationBehavior()
during click
, vs listening for activateclick
. It's probably a micro-optimisation in the case of click, but I like the declarative nature of this and the performance potential it has.
What problem are you trying to solve?
I want to add activation behavior to the above button, such as "when this button is clicked, toggle this checkbox". As expected on the platform, I don't want this action to happen if a listener calls
event.preventDefault()
.What solutions exist today?
This isn't great because the default may be prevented by a listener further along the path.
This isn't great because the effect may happen a frame later (
rAF
isn't a reliable solution here due to https://github.com/whatwg/html/issues/10113). It's also observably async:Other techniques, like adding listeners to
window
may be missed due tostopPropagation()
, and may still be 'beaten' by another listener.How would you solve it?
Something like:
Anything else?
No response