Does javascript event handling occur inside or outside the program flow?
Asked Answered
S

1

11

This question is related to Javascript event handling and flow control, but it is one step beyond. The question that remains unanswered is: when an event is fired and control is returned to the browser, could the browser decide to handle other events first (fired by other scripts or user action) (A), or will it always handle my event directly (B)?

Does javascript event handling occur outside (A) or inside (B) the program flow? The question is important, because in case (B) you can rely on the fact that nothing has been changed between firing the event and the event handler, while (A) gives no guarantees whatsoever.

My first guess is (B), how else could stopPropagation() and preventDefault() work? But giving it a second thought, it is no hard evidence.

A real-life example of this problem. I am modifying a rich text editor (hallo), and I want it to have these specs:

  • clicking on an editable text (#txt) will activate the editor, and clicking outside #txt will deactivate it. hallo uses blur and focus events on #txt to achieve this.
  • Activating the editor opens a toolbar, mousedown on the toolbar (but not on a button) will set a flag that prevents the blur event on #txt to deactivate the editor. The toolbar will return focus to #text.
  • mousedown on a toolbar button should also prevent deactivating the editor, but it should first wait till the click event, perform its action and then return focus to #txt. Some actions are immediate (bold or italic), while others need extra user input (selecting from a dropdown).
  • Some of these buttons open a dialog.
  • ...And I want all these elements (editor, toolbar, dialog) to be modular and easily extendable.

Now in most cases when you close a dialog you want the focus to return to #txt. But in the case where a dialog is opened and you click somewhere else on the page, the editor will close and call the toolbar, including the dialog to close as well. If in this case the dialog would return focus to the editor, it would activate the editor again.

As far as I understand now, the event handling order is at least deterministic. It is not possible that some event gets a delay while others are processed earlier. This is what is meant by 'synchronous'. The exception is of course events like loading a file.

From the perspective of a program component, say the dialog, the situation can be quite unpredictable. It can bind a handler to the open event, and then call dialog("open"), but anything can happen between the call and the handler, if only because the editor has an event handler on the same event.

So my conclusion is 1) yes it is predictable but 2) it needs a sophisticated architecture to implement this.

Stunsail answered 1/6, 2012 at 9:59 Comment(9)
Isn't this trivial to test by adding a few console.log() statements in your openDialog() and doThing() functions?Amberjack
@lanzz: a single example of (A) would definitely prove (A), but so far I only found (B), or rather, so far I did not catch other event handlers between the first and the second. Unfortunately that proves nothing.Stunsail
What's the code for openDialog? It's not like you registered two click events on the same node set, so I don't think you would get (B)Basketry
How are you triggering the open event? It does not seem to be a standard DOM event, so it is probably handled by jQuery as a custom event, and most likely is executed in-flow.Amberjack
@jack, openDialog could be a jQuery UI function like $('div').dialog("open").Stunsail
@davin, yes, keyboard events are a different matter because they already are generated outside the program flow context and could invoke multiple click handlers in an order that is difficult to predict. Thefore I used an event that is generated in code.Stunsail
@lanzz, I assumed jQuery uses javascripts onboard event handling. If not, I would be curious to know both, jQuery as well as default events.Stunsail
a more whimsical formulation of my question would be: when you got the attention of the browser, do you give it away by calling an event?Stunsail
@Carlo, jQuery doesn't use the native event model transparently, it has an internal model, and hooks onto the native one. You can see this clearly in the jQuery source. One major reason for this, is that that event model doesn't guarantee order of execution, but jQuery does, because it maintains it's own array of registered handlers. This however doesn't change the answer to the question.Institution
I
4

In general the event model is synchronous and reentrant, meaning that if an event handler causes another event - the second event will be executed synchronously and only after completion will the first event continue executing.

This seems to be what you're trying to describe, in which case (B) is guaranteed.

Relevant source: http://www.w3.org/TR/DOM-Level-3-Events/#event-flow

Institution answered 1/6, 2012 at 10:37 Comment(8)
I believe that just because the event model is synchronous and reentrant, (A) is true. The open event on the dialog may be handled by many event handlers, which in turn can invoke other events that are handled before the program returns. So between opening the dialog and its handler, ANYTHING could happen (?). Except that exactly jQuery, according to you, has its own model.Stunsail
The difficulty is that "synchronous and reentrant" is seen from the perspective of the browser. From the perspective of the program it is different. To be honest, I can't get my brains around it.Stunsail
@CarloRoosen, I'm not sure what you mean. You need to explain all these terms you're using. Different perspectives? I don't really understand what you mean. Try and think of a specific example where (A) would enforce one behaviour and (B) would enforce another, and maybe it will be easier to answer your question. In any case, it's true that opening the dialog may cause other events to happen, which could potentially change things before returning to the click handlers. Although that should be obvious, any alternative model would either be non-deterministic or useless (or both).Institution
It should be obvious, because the browser has no concept of what handler should invoke what other handler(s), so it's not going to call one handler first because another handler caused some event. That would be overly complex, and I can't see how that would make sense. In your particular example it also wouldn't make sense because of how jQuery handles events, although that's a separate issue.Institution
Synchronous and reentrant is merely a way of saying it's blocking and nested. So if event A has handlers A1, A2, ... An, and Ak causes event B, who has handlers B1, ... Bm then the execution order looks as follows: A1, A2, ..., Ak, B1, B2, ..., Bm, A(k+1), ... An. That implies potentially infinite nesting, so if a handler of B causes C you recursively apply the same logic. Finally, note that the handler order of execution (as I mentioned earlier) is not guaranteed in the pure event model.Institution
I added my real life problem to the question. I am not sure whether (A) would enforce one behaviour and (B) would enforce another, maybe my question needs to be restated.Stunsail
"the handler order of execution (as I mentioned earlier) is not guaranteed in the pure event model". You mean that {A6, A3, ..., A1, {B2, B8, ..., B1}, A2, ... A4} is also possible (outside jQuery)?Stunsail
@CarloRoosen, regarding order, yes, that's exactly what I mean.Institution

© 2022 - 2024 — McMap. All rights reserved.