Skip to content

Note: Implementation uses explicit event delegation [sticky] #801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
krausest opened this issue Sep 28, 2020 · 19 comments
Open

Note: Implementation uses explicit event delegation [sticky] #801

krausest opened this issue Sep 28, 2020 · 19 comments

Comments

@krausest
Copy link
Owner

krausest commented Sep 28, 2020

This note (and it should be regarded as a note, not an issue) marks implementations that use explicit, i.e. manual, event delegation.
The note is somewhat controversial since there are multiple views on it:

  • It's natural and best practice to use manual event delegation in vanilla js and in frameworks with a low abstraction level
  • It's the fastest approach for all frameworks
  • It doesn't show the cost of implicit, i.e. framework provided, event delegation that's typical available for frameworks with a higher level of abstraction. If a framework has such an implicit event delegation this mechanism should be measured in this benchmark. Otherwise we're only comparing vanillajs variations and not the frameworks. (And we already know the performance of vanillajs, so it doesn't add any value.)

Advice: Use whatever fits the idiomatic style of your framework, but please don't over optimize. This note adds a litte pressure to prevent over-optimizations.

@brainkim
Copy link
Contributor

brainkim commented Nov 23, 2020

Any progress on this issue? I’m realizing that assigning DOM onclick properties is eating a significant chunk of time on tests like rowlots. Is there a sliding scale? Am I allowed to read the row id off the DOM via ev.target? Will I be penalized if I have one event listener on the table vs one for each row? My framework doesn’t implement any sort of framework provided event delegation so I would argue it is idiomatic to use some kind of event delegation mechanism when you have a parent with many children.

Also, does anyone have any microbenchmarks for whether onevent properties are faster or slower than addEventListener calls? It would be a shame to have to switch for performance, I think virtual DOM libraries are actually the perfect use-case for onevent properties.

@ryansolid
Copy link
Contributor

We don't seem to be marking libraries for this currently. So I don't believe there is any repercussion to doing so? Atleast today. #772 is more about manipulating the selection class directly.

I've argued against this being penalized since it is the defacto way to solve events in a large table. Either the framework does it, or you do it. Some libraries actually have this in the documentation as being the way to do it. Once you get to a certain point on the table everyone is using event delegation. It isn't like it is fundamentally changing the nature of the test.

I think this is sort of gray area in that it makes the implementation look less clean so it's discouraged. It breaks the abstraction wall. But it's hard to penalize someone for something that almost every other library is doing. I do see the argument that if we are critical on libraries using imperative escape hatches to directly manipulate DOM elements in ways the library already has means to but are being avoided purely for performance reasons, that being fine reading from them is a very subtle distinction to make when the library already has a way to attach events. To be fair explicit event delegation still generally uses the libraries event attaching mechanism, it just involves manual traversal of DOM trees.

But I think we need to decide if this is something worth penalizing on. And if not, leave to the implementor. I once made an event delegated version of Svelte but I wasn't comfortable merging it without Svelte community or Rich's blessing. I never got it so I retracted my PR.

@brainkim
Copy link
Contributor

brainkim commented Nov 23, 2020

It’s odd because it’s one of those optimizations that anyone working with large amounts of DOM nodes will go to first, but I understand the arguments against explicit event delegation. Specifically, you break encapsulation between your table and row components if the row cannot hide its internal DOM structure because the table is reaching into the row during click events. Then again, I don’t think any “row component” should be written in isolation from a table or tbody component, as I don’t think a generic tr component is very useful. Also, there are other cost cutting measures you can use which break encapsulation between the table and row components, which we don’t presumably care about, like assuming the item id passed in will never change for keyed implementations.

I once made an event delegated version of Svelte but I wasn't comfortable merging it without Svelte community or Rich's blessing. I never got it so I retracted my PR.

Is it just me or does Svelte have an even stronger case for event delegation because it doesn’t define a separate component for rows?

@ryansolid
Copy link
Contributor

@brainkim
Yeah reactive libraries do not need a row components to handle the change detection (we need to build the guards into any loop iteration since it can be just as inefficient with anything component or not). And admittedly in reactive libraries are where the greatest use of explicit event delegation has been seen here. That being said if your framework is about writing less code having the implementation not write less code is equally concerning. I never got a response in either case. But I think Svelte is going for an aesthetic as much as performance.

@krausest
Copy link
Owner Author

I wouldn‘t call it „penalize“. It‘s not an error like #694, it‘s just a note that the implementation might take advantage from a user code event delegation.
If that‘s the style of your framework you shouldn‘t hesitate to submit it.

@brainkim
Copy link
Contributor

In the end I decided against doing event delegation because it wouldn’t be in good sport. Also finding the row id and making sure the target contains a specific tag seems non-trivial.

krausest added a commit that referenced this issue Mar 13, 2021
@krausest krausest changed the title Note: Implementation uses explicit event delegation Note: Implementation uses explicit event delegation [sticky] May 2, 2021
@syduki
Copy link
Contributor

syduki commented Nov 8, 2024

This issue requires revision, since aside from opinionated labeling, it currently lacks any value.
Thus, straight to the point:

  1. Remove the word "explicit" in the caption.
  2. Put this label on any framework that uses event delegation (no matter "explicit" or "implicit").
  3. Make an automated test for this feature detection (to exclude human factor).

The reasoning behind this is below.

Historically this label, and actually any label here, was viewed as a problem, even now when this was changed to "Note" it is viewed as a bad spot.
Another fact is that the labeling itself is left at the discretion of implementer, thus many frameworks doesn't include it at all, some deliberately to not fall into that bad spot while others unknowingly due to lack of concern about benchmark rules.
Now about "explicit" or "implicit", these words have nothing with framework performance but developer experience, which obviously is not something tested here. Moreover, having that "explicit" word here implicitly pushes the "implicit delegation" to be perceived as holy grail of application/framework performance optimization, and maybe a best practice, which is naively optimistic subjectively and technically plain wrong.
Actually, no matter how event delegation is achieved, the main reason of it's existence is the performance gain, and actually, in real applications, the "implicit" delegation (which often is no opt-out) is a bigger concern for developer experience than "explicit" one (which is always opt-in). Thus it would be of more value to have it labeled everywhere, then anyone looking at that label would know that there is a performance optimization technique involved, with some potential pitfalls.

@ryansolid
Copy link
Contributor

ryansolid commented Nov 8, 2024

Now about "explicit" or "implicit", these words have nothing with framework performance but developer experience, which obviously is not something tested here.

I disagree. The thing is every framework can escape out of its mechanics to do something more effectively here by just using the underlying DOM and JS. That makes the benchmark uninteresting. That was the problem the benchmark was having. Solid used to not do event delegation, so we'd do this thing where we captured the element and looked for the closest TR etc.. it was very bulky. Then you'd have React which just applied an event handler. Now React could have done explicit event delegation too and it be even more performant but who would do that when the built in was pretty good?

Moreover, having that "explicit" word here implicitly pushes the "implicit delegation" to be perceived as holy grail of application/framework performance optimization, and maybe a best practice, which is naively optimistic subjectively and technically plain wrong.

Now I get the argument that now automatic event delegation is seen a very different light than 4 years ago. It very much was seen as important because of historical fact that it alleviated issues like differences between browsers off the end developer. These days it has become so ingrained that it is pretty fundamental modern frameworks to handle things like Portals and optimal hydration.

So it can be argued that event delegation within the confines of the framework is if so chosen a very good thing. It's an interopt tradeoff. I think there could be a flag for any event delegation but the one in this issue is specifically to point at doing manual DOM operations in user space for the sake of performing better in the benchmark. That is important to be aware of. Although personally I never let this one discount implementations in the benchmark. We can have opinions whether or not frameworks should be using event delegation but whether this benchmark is concerned with that is debatable.

@krausest
Copy link
Owner Author

krausest commented Nov 8, 2024

This benchmarks gives the best insight when implementations look like in the framework's docs since all other implementations converge against vanillajs.
The note is meant to penalize implementations that cut corners just to squeeze out the last 0.1 in this benchmark. And I really think we should keep those notes for that purpose.

@syduki
Copy link
Contributor

syduki commented Nov 8, 2024

this issue is specifically to point at doing manual DOM operations in user space for the sake of performing better in the benchmark.

I think this argument now is obsolete, it may have been so in the early days of existence of this benchmark, nowadays even the most miserable frameworks have enough abstraction for masking manual DOM operations, yet having perfectly working delegation outside of the framework, which cannot be classified as explicit by the above definition, either implicit. Thus now explicit is specifically about the delegation as a feature in user space and not its implementation details. Maybe there are few cases when manual DOM operations are used to delegate events but those would be more appropriate for 772.
Now about user space, the only thing implicit delegation has to offer, as was already said, is encapsulation, but that does not make it automatically an idiomatic style of framework. I would not be surprised to see such idiomatic style "micro-optimizations" where one uses an explicit delegation in user space even if the framework has an implicit delegation.

@syduki
Copy link
Contributor

syduki commented Nov 9, 2024

This benchmarks gives the best insight when implementations look like in the framework's docs since all other implementations converge against vanillajs.

This comment made me to dig into the docs of certain frameworks, though that was mostly a waste of time, I have yet to see such framework's docs specifying the idiomatic style of event handling/delegation. To my knowledge it is always at discretion of user which style/paradigm to adopt. It seems that here the concern is more about style than technical details, because it's incomprehensible how delegation, which is a programming pattern, can converge into vanillajs which basically means JavaScript API. This is like saying that a song coming from an outdoor speaker is explicit sound compared to indoor speaker, and it converge into singer.
But seriously, to stress this once again, from point of view of performance benchmark there is no value in this flag at the moment.
Let's consider the case of Svelte, where they completely redesigned the event system, so instead of old opt-in-explicit-delegation now there is no-opt-out-implicit-delegation. When looking at the benchmark table one may see that Svelte got a major performance boost, and only chosen ones will think that actually that gain is basically because of delegation and in reality there may be no gain in performance at all, but for the rest of mortals that is not so obvious when looking at those results as there is no indicator about that feature.
Another thing are the "hot rod"-like frameworks. Let's look on the fastest one - doohtml. We can see there the 772 label but no explicit delegation label, and one will never mind, thinking that there is an implicit delegation, but no, it is really an explicit one, albeit not so explicit as in sinuous, you have to analyze the implementation source code to catch that.
There are more examples to be given, but these above should be enough to get the picture. If this still is not convincing then let it be so.

@krausest
Copy link
Owner Author

Please take a look at the new implementation of strve-rv: https://github.com/krausest/js-framework-benchmark/blob/master/frameworks/keyed/strve-rv/src/main.jsx#L122-L131
I think such implementations deserve some kind of note just to avoid that all other implementations go the same way. You can do it but it's cutting corners. And that should be mentioned.

@maomincoding
Copy link
Contributor

Please take a look at the new implementation of strve-rv: https://github.com/krausest/js-framework-benchmark/blob/master/frameworks/keyed/strve-rv/src/main.jsx#L122-L131 I think such implementations deserve some kind of note just to avoid that all other implementations go the same way. You can do it but it's cutting corners. And that should be mentioned.

Thank you for pointing out the point of explicit event delegation. I did not display the mechanism of automatic event delegation in the framework. I would like to enable users to manually optimize it. I specifically wrote this code for benchmark testing. If we consider the accuracy and rigor of benchmark testing, then my approach is indeed not very good for other frameworks that have done automatic event delegation. We should not excessively manually optimize for the sake of optimization, as it would lose the meaning of testing.

@syduki
Copy link
Contributor

syduki commented Nov 11, 2024

I think such implementations deserve some kind of note just to avoid that all other implementations go the same way. You can do it but it's cutting corners.

This "rule" penalizes basically not only implementations but the underlying frameworks for not having built-in delegation, forcing the encapsulation style of events definition, which by the way is an anti-pattern from the performance point of view, and this whole thing is reasoned as cutting corners. I just wonder, what if writing like that in a framework which have implicit/hidden/automatic/whatever non-explicit delegation, performance wise it would not matter and there would be only a delegation style concern, would that also be regarded as cutting corners? If not then maybe a new label is needed for that, like "implementation uses non-idiomatic style of event delegation"?
It seems to me that you don't have the whole picture of the scale of this issue here, where many frameworks implemented implicit delegation for "click" event only, some only for a subset of pointer-type events, just to comply with this benchmark, which actually is real badass corners cutting, and from this perspective the issue title is misleading and should be changed to "explicit click event".
That strve-rv example is a common one, and by the way it is idiomatic style with no direct DOM manipulation as in doohtml. I see that it is the easiest "prey" for such labeling, and the fear of bad spot may push it to move that handling in the framework like many did, or maybe as an add-on just to have it under the rock.
There was claiming here recently about React being slow, and such mentality like here is one of the reasons of it, when in a performance oriented application, one is pushing suboptimal/underperforming code for the sake of style, reasoning that by no corners cutting, when actually any average tutorial will teach you that you have to delegate the events in some way if performance is a concern. Sure, that is not in the docs, because no docs will teach you about such peculiar thing as how to implement an idiomatic event delegation, or that you have to do delegation to have performance when that is a common sense (yes, Solid docs are an exception, but it is/was also a major advertiser of this benchmark thus out of scope).
I am not saying that this label should be removed, what I'm saying is that it should be an indicator of performance tweaks, not of coding style/pattern as currently it is, and for that it should be put on any framework which uses these tweaks. That will be of much more value, as one will clearly see that, let's say React, is not using delegation but Svelte 5 does.
Ultimately, the delegation itself, no matter explicit or not, is not a standard but an implementation-dependent technique with its side effects, from this point of view is of the same concern as non-keyed frameworks.

@syduki
Copy link
Contributor

syduki commented Nov 11, 2024

If we consider the accuracy and rigor of benchmark testing, then my approach is indeed not very good for other frameworks that have done automatic event delegation.

so we are witnessing exactly this:

the fear of bad spot may push it to move that handling in the framework like many did, or maybe as an add-on just to have it under the rock.

as a result: #1770

A perfectly valid code was adapted just to comply with the style "rule". Now that will be a suboptimal/underperforming code no matter what approach was used under the hood.
In the end of the day the event delegation is not a rocket science, just a single line of code that can be spit in any framework:

window.addEventListener('click', e => e.target.onClick?.(e));

And that is what now this label is asking for, do your weird thing in your framework and we are good, trying to be delicate and explicit - here is your #801, you deserve it.

@maomincoding
Copy link
Contributor

If we consider the accuracy and rigor of benchmark testing, then my approach is indeed not very good for other frameworks that have done automatic event delegation.

so we are witnessing exactly this:

the fear of bad spot may push it to move that handling in the framework like many did, or maybe as an add-on just to have it under the rock.

as a result: #1770

A perfectly valid code was adapted just to comply with the style "rule". Now that will be a suboptimal/underperforming code no matter what approach was used under the hood. In the end of the day the event delegation is not a rocket science, just a single line of code that can be spit in any framework:

window.addEventListener('click', e => e.target.onClick?.(e));

And that is what now this label is asking for, do your weird thing in your framework and we are good, trying to be delicate and explicit - here is your #801, you deserve it.

This reminds me of the difference between manual and automatic transmission in cars.

@syduki
Copy link
Contributor

syduki commented Nov 11, 2024

This reminds me of the difference between manual and automatic transmission in cars.

That comparison is not even a close, the difference there is huge technically, not that much stylistically.

Here the talk is just about the look of a naked quacking duck (read delegated event).
If it lives in the courtyard (explicit content visible) then it is a #801 duck type, if we put it in a box - it is an anonymous duck. At some point you may feel disgrace with your #801 duck, but as an empathetic human being you don't want to put it in a box. So all you left is to get rid of it, changing it for some cute fluffy little ducklings, albeit not that noisy as the naked one.

@maomincoding
Copy link
Contributor

Benchmarking is not only about performance scoring, but also about promoting one's own framework.
Additionally, I realized that the positioning of each framework is different. For example, my framework pursues flexibility and ease of use, but if I cannot master this level well, the design will be difficult to understand. Therefore, the trade-off between frameworks is crucial.

@syduki
Copy link
Contributor

syduki commented Nov 12, 2024

Benchmarking is not only about performance scoring, but also about promoting one's own framework.

I don't think such discussions should get into this thread but I will reply as an exception.
So, actually a benchmark is KPI measuring thus is about performance only, as technically that is the only measurable objective aspect. All other aspects are pure subjective. Promotion/advertising things like easy of use, beautiful, feature-rich, flexible, painless should not be in the scope of any benchmark, those are opinionated non-measurable features intended for surveys.

This was referenced Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants