Comments (5)
@drjayvee, thanks for proposing this suggestion and providing the example repo. YUI's approach feels very similar to how Flex handles bubbling and cancelable events. I had a few questions while reviewing everything I hope you would clear up.
First, I like how straight-forward it is to add bubble targets. Simply declaring bubble targets as children components are added seems natural. One thing that seems to be missing is the ability to create a capture phase in addition to bubbling due to how the map is created. Could you maybe describe how a capture phase and bubbling phase could be implemented? Also, because any object can register itself as a bubbling target I assume we will need to add cyclical checks to guard against infinite loops.
I also like the separate Dispatcher class since it enables developers to extend their own custom dispatchers. How common is it for a YUI project to extend Dispatcher to add custom behaviors? Do you think custom dispatchers could achieve the remaining functionality (i.e. cancel, stopBubbling, before, and after) if we provided hooks into certain states? My thought here is we could provide a simple Dispatcher and allow users to add their own "before all", "before", "after", and "after all" states. It may also be worthwhile investigating treating the dispatcher almost like a state machine, since this is what the conditional handling of "after" seems to accomplish, the addition of a "before" state could also allow the ability to transform, filter, or reject events.
Finally, one of my biggest concerns with events that may be canceled is it can be difficult to conceptualize what happens to events after they're canceled. In EventTarget it appears that canceled events will continue to bubble. Even if we were to prevent bubbling canceled events we would at least need to allow events to be dispatched to all sibling event handlers; otherwise handling events, and in turn object state, becomes based on the order that event handlers were added. I and many other developers saw this issue in Flex, that canceling an event didn't stop an event and if other handlers didn't check to see if the state of the event was canceled then undesired effects may happen. Flex tried to solve this by offering stopPropigation()
, stopImmediatePropigation()
, and priority queues, adding to the complexity. My personal feeling is if we only provide a simple Dispatcher then it is better to relegate cancelability to data, however, if we create a more complex Dispatcher with before and after hooks it would seem that we could use the before handler to filter out events in that phase.
Overall I think YUI's approach is a good starting point. Event bubbling is very promising and I'm sure we can work through the rest. I'd like to get some of these questions answered and hear your feedback to refine the approach. Thanks again 👍
from core.
@devpaul thanks for your response and questions. It's nice to see this ticket getting some attention!
Event capture
One thing that seems to be missing is the ability to create a capture phase in addition to bubbling due to how the map is created. Could you maybe describe how a capture phase and bubbling phase could be implemented?
Regarding event capturing, the short answer is that YUI's EventTarget doesn't support it, so I haven't implemented it. 😄
In general, Javascript developers are used to bubbling because that's the DOM's default method. Capturing is available, however, and it makes sense for a library to offer both alternatives, I guess.
If dojo followed the DOM (and event listeners are registered for either bubbling or capturing), then capture support can always be added later, since that won't break the bubbling-only API. Therefore, I would propose to implement bubbling-only now and add capture later (if desired).
One thing to reconsider is the name of the addBubbleTarget
method. If "bubble" targets can also capture events, maybe the name should change to make that clear.
Also, because any object can register itself as a bubbling target I assume we will need to add cyclical checks to guard against infinite loops.
I hadn't thought of that. I agree that infinite loops must be prevented.
Custom Dispatch
I really only wrote a separate Dispatch class to make the code readable, not because I intended for custom implementations to be used. I have no idea whether that happens in YUI3 implementations (I know only one...), or whether it's even possible. Therefore, I haven't considered the alternatives and features that you mention.
The Dispatch
class in the current implementation doesn't know what bubble targets are present, so a custom implementation can't simply implement capture by changing the dispatch
method. Some refactoring is required to enable more complicated event flows.
I guess the logic in EventTarget._fireEvent
could be moved to Dispatch
to enable some customization of the event flow.
Alternatively, EventTarget
could construct a graph of targets (and detect loops in the process!) and walk the graph in _fireEvent
to implement different ordering of targets for capture / bubbling.
Event cancelling
In EventTarget it appears that canceled events will continue to bubble.
I have to admit I haven't thought about the state machine too well, and have implemented it differently from YUI3.
You're right that canceled events continue to bubble. They also are delivered to other subscribers! In my implementation, Event.cancel()
has the same effect as Event.preventDefault()
in YUI3. Maybe the name cancel
isn't well chosen here.
YUI3's EventTarget offers stopPropagation
and stopImmediatePropagation
methods, which change the flow of an event.
I haven't thought in depth about what would be both an intuitive and powerful feature set here, and have not implemented any features that change the flow of events. (The only feature in my implementation is that after
subscribers are not called if an Event has been cancelled.)
I and many other developers saw this issue in Flex, that canceling an event didn't stop an event and if other handlers didn't check to see if the state of the event was canceled then undesired effects may happen.
Here's where after
subscriptions are extremely useful! These only get notified if an event has not been cancelled in the on
(or before
) phase. Events in the after
phase are not cancelable.
Therefore, users should generally subscribe after
to prevent exactly the problem you're describing. You only subscribe on on
if you need the ability to cancel events, which is the exception, not the rule. YUI puts it this way:
Use after() to subscribe to events with a default behavior when you want to react to the event with a side effect. Use on() to subscribe to events if you need to prevent or alter the default behavior or if they don't have default behavior.
Conclusion
I've used YUI3's EventTarget (and AttributeObservable) a lot (but only on one project), and love how expressive and powerful, yet simple and intuitive code using these APIs is. What I've implemented is mostly the feature (sub)set I use(d). I do appreciate that YUI3's design might not provide enough flexibility for some.
The challenge is to select a feature set that is powerful enough to enable different patterns, yet simple enough to learn and mentally reason about. (Priority queues for event subscriptions sounds awfully complicated in that regard IMHO.)
Since I'm only familiar with YUI and have no experience designing libraries for other users, I'm not sure I can be of much help.
from core.
There have been a number of times in the past that I have wished dojo/Evented
could do a little bit more, so it's nice to see these efforts.
Following on the heels of the previous comments, a capturing phase could be implemented similarly to how dojo-core/aspect
separates "before" and "after" advice (and with a syntax not unlike that of EventTarget.addEventListener
. The on
method could have a useCapture: boolean = false
argument that, when true, adds the callback to a separate registry to be executed before the bubble listeners. Each of those listeners would also have the ability to stop the event from propagating (in which case, CustomEvent#stopBubbling
could be renamed CustomEvent#stopPropagation
).
The main difficulty is efficient execution, since either the event would first have to bubble up before executing in order to determine the top-level object, or some sort of caching mechanism would need to be used to store an object tree.
That said, I am still trying to wrap my mind around how all of this would fit in with/affect the the ideas being formulated about what a Dojo 2 application will look like. The idea is that individual components (whether that be a widget or some other object) would create cancelable actions, which then would update a store that reports back to components listening for updates. This emphasis on single-direction data flow differs from how a lot of applications were/are built with Dojo 1. With this in mind, do you have any ideas on how this additional event system might be used to improve this unidirectional data flow?
I sincerely hope this question does not come across as dismissive, especially since I can think of a few projects where something exactly like what is being proposed here could have solved certain unpleasant problems. However, since the target audience for Dojo 2 is teams developing large, enterprise applications, providing them with tools that allow them to depart from decided-upon conventions could be confusing.
from core.
I'm not sure that capturing makes that much sense, actually. It works well in the DOM because parentNodes are their childrens' bubble target by default. That means that pretty much every event can be captured by the topmost node.
I doubt there will be node chains / trees in an application, unless the framework makes that the default structure.
I really can't comment on what would work for Dojo 2 applications. I've never written an enterprise-level application, nor have I ever used Dojo 1.
I do have a nice example of the usefulness of YUI's EventTarget API: radio button groups.
First consider ToggleButton
, which has a pressed
state, and updates its DOM node after('pressedChange')
.
The ButtonGroup
has ToggleButton
children. The group subscribes using on('pressedChange')
so it can cancel "unpress" changes (in a radio group, you cannot unpress the pressed button).
This is a nice example of the on
/ after
subscription model with cancel
able events.
When a button is added to the group, the group adds itself as that button's bubble target, so bubbling is used as well. (This is mostly for convenience: the group could subscribe to buttons individually, of course).
Obviously, this model isn't just for tiny systems like button groups. But whether it works as the basis for an enterprise-level application framework I really don't know.
from core.
This is a long running topic. I don't think it is closed and it is related directly to dojo/compose#67.
I am going to close for now and I think we should focus on making dojo/compose#67 a success. If that causes features/changes in dojo-core
great, but we should track those as new issues.
from core.
Related Issues (20)
- Update tests to Intern 4
- Deprecate (maybe remove) load
- Deprecate lang/assign (and refactor usage)
- old dojox.storage documentation is broken HOT 2
- `utils#throttle()` test fails in Edge HOT 1
- Xhr unit tests intermittently fail on IE11
- I just would like to use dojo2 as a library, it looks a little complex, how could I use it like a react or dojo1? HOT 9
- tslib must be a dependency and not a devDependency HOT 8
- Evented is bloated
- Problem since TypeScript 2.7.1 – Type 'M[keyof M]' does not satisfy the constraint 'EventObject<keyof M>' HOT 9
- UrlSearchParams toString() should not add queryParams if they have no value set
- has.ts causes loading the main bundle to crash in Internet Explorer 11 in https HOT 4
- Support the abortable fetch API HOT 1
- dojo/widget-core typo in code snippet HOT 1
- README: outdated Promise doc
- Docs for UrlSearchParam are wrong
- Upload progress events add preflight request
- Standardize Constructor exports
- Evented listener destroying its own handle won't run all listeners
- lang.mixin errors with null target
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from core.