ISSUE-35 - Public View
DOM3EV: dispatchEvent vs event state (ISSUE-20 take two)
- State:
- CLOSED
- Product:
- DOM 3 Events
- Raised by:
- Bjoern Hoehrmann
- Opened on:
- 2006-03-05
- Description:
In the now resolved ISSUE-20 we've discussed some of the interactions between Event.preventDefault, Event.stopPropagation() and dispatchEvent, in particular what happens if an event object is dispatched after an initial dispatch has been completed. It seems additional clarification is necessary. For the purposes of this issue, an implementation is assumed to dispatch event objects using dispatchEvent() and an event is considered to be currently beeing dispatched from the point where control is passed to the dispatchEvent() implementation and when the method returns. stopPropagation, stopImmediatePropagation, and preventDefault modify the state of the event object. When these are called on an event object that is currently beeing dispatched, this yields in well-defined results. The concern is what happens a) if these are called before initial dispatch of the object, for example <form id="test" action="data:,SUBMITTED"></form> <script> var evt = document.createEvent("Events"); evt.initEvent('submit', true, true); evt.preventDefault(); document.getElementById('test').dispatchEvent(evt); </script> and <form id="test" action="data:,SUBMITTED"></form> <script> var evt = document.createEvent("Events"); evt.initEvent('submit', true, true); evt.stopPropagation(); document.getElementById('test').dispatchEvent(evt); </script> For the first case, some implementations dispatch the event as expected, and the effect is as if preventDefault was called from some listener, i.e., the default action is prevented. In the second case, some implementations dispatch the event by not triggering any listeners and performing the default action. This is about what I would expect from the current text. b) for dispatchEvent(an event currently beeing dispatched) this is well-defined in the draft, you get an exception. This is new in DOM Level 3 Events and not so widely implemented, some implementations simply ignore the method call. I think what the draft says is fine. c) when dispatching a dirty event object var evt = document.createEvent('MouseEvent'); evt.initEvent('click', ...); for (...) target.dispatchEvent(evt); is one case for this. If one of the click listeners prevent the default action of the event or stop it's propagation, would dispatchEvent reset this state? Implementations appear to be most inconsistent for this case and we resolved that yes, dispatchEvent would reset the state. Concerns have been expressed regarding the asymetrie with how this works for event objects that have not been dispatched yet. One thing to note here is that the current draft notes that calling initEvent() on an event object that is currently beeing dispatched, or has been in the currently beeing dispatched state at least once, has no effect. So if neither initEvent nor dispatchEvent reset those states, <p id='test'>0</p> <script> var test = document.getElementById('test').firstChild; function update(evt) { test.nodeValue = String(Number(test.nodeValue) + 1); evt.stopPropagation(); } document.addEventListener('bh-update', update, false); var update = document.createEvent('Events'); update.initEvent('bh-update', true, true); setInterval(function(){document.dispatchEvent(update)}, 500); </script> would call update() only once. This is indeed the case for e.g. two weeks old experimental versions of Firefox and Opera9, though removing the evt.stopPropagation() would yield in the same result in Firefox, which insists that initEvent is called again. So if this is changed to setInterval(function(){ update.initEvent('bh-update', true, true); document.dispatchEvent(update)}, 500); it would work as "expected" in both browsers. Unfortunately these implementations ignore that initEvent must have no effect, so if you have setInterval(function(){ document.dispatchEvent(update); update.initEvent('bh-update2', true, true); }, 500); they would change the Event.type to bh-update2 and never call the listener again. It's the same if initEvent gets called while the event is currently beeing dispatched, so <p id='test'>0</p> <script> var test = document.getElementById('test').firstChild; function update(evt) { test.nodeValue = String(Number(test.nodeValue) + 1); evt.initEvent('bh-update2', true, true); } document.addEventListener('bh-update', update, false); var update = document.createEvent('Events'); update.initEvent('bh-update', true, true); setInterval(function(){document.dispatchEvent(update)}, 500); </script> would increment to 1 and then stop... A slightly different case is <body> <p id='test'>0</p> <script> var test = document.getElementById('test').firstChild; function update(evt) { test.nodeValue = String(Number(test.nodeValue) + 1); evt.stopPropagation(); evt.initEvent('bh-update', true, true); } document.addEventListener('bh-update', update, false); var update = document.createEvent('Events'); update.initEvent('bh-update', true, true); setInterval(function(){document.body.dispatchEvent(update)},500); </script> </body> this proofs that initEvent does not reset the propagation state, the listener would be triggered just once. Then, for <body> <p id='test'>0</p> <script> var test = document.getElementById('test').firstChild; function update(evt) { test.nodeValue = String(Number(test.nodeValue) + 1); evt.initEvent('bh-update', false, false); } document.body.addEventListener('bh-update', update, false); document.addEventListener('bh-update', update, false); var update = document.createEvent('Events'); update.initEvent('bh-update', true, true); setInterval(function(){document.body.dispatchEvent(update)},500); </script> </body> we can take note of the fact that Firefox does [0,1,2] and Opera9 does [0,1,2,3,...], hmm, and <body> <p id='test'>0</p> <script> var test = document.getElementById('test').firstChild; function update(evt) { if (evt.currentTarget === document) test.nodeValue = String(Number(test.nodeValue) + 1); evt.initEvent('bh-update', false, false); } document.body.addEventListener('bh-update', update, false); document.addEventListener('bh-update', update, false); var update = document.createEvent('Events'); update.initEvent('bh-update', true, true); setInterval(function(){document.body.dispatchEvent(update)},500); </script> </body> gives [1] in Firefox and [0] in Opera9. I guess the interpretation for that is that initEvent can be used to emulate stopPropagation() in Opera9 but can't be used that way in Firefox... Now, these features aren't that widely used and implemented and the cases mentioned above are quite some edge cases, so we are probably relatively free to pick a sensible solution for this. I note that if implementations keep some internal state like whether the event has been generated by the implementation, dispatchEvent would have to reset at least this state, otherwise it would be possible to trick the user into entering [/etcpaswd] and submit that to a file upload control if that's allowed at all, for example. So one way to resolve this would be to say there are two states, when the event is currently beeing dispatched and when it's not beeing dispatched. If it's not beeing dispatched, DOM applications can mutate the event as they like and dispatchEvent would honor that. If it is beeing dispatched, initEvent() has no effect, preventDefault() etc work as they should. dispatchEvent would, after all listeners have been processed, i.e., immediately before it returns, reset the propagation and .defaultPrevented states, and return. This would be roughly what implementations do, what we've resolved, and what you might expect, while retaining some level of sanity. The downside of that would be that Event.defaultPrevented is always false immediately after dispatchEvent() returns, so applications would always have to check the return value of dispatchEvent to decide whether to perform the default action; this could easily be addressed by a note for .defaultPrevented though.
- Related emails:
- ISSUE-35: DOM3EV: dispatchEvent vs event state (ISSUE-20 take two) (from dean+cgi@w3.org on 2006-03-05)
- Re: ACTION-76: Build test case for understanding behaviour of redispatching cancelled and stopped events (from derhoermi@gmx.net on 2006-03-15)
- Re: DOM-Events: Non-bubbleable means non-capturable? (from derhoermi@gmx.net on 2006-03-24)
- Minutes, face to face meeting (from chaals@opera.com on 2006-05-15)
Note: Some of these links may be accessible only to W3C Members.
Related notes:
No additional notes.