The preventDefault on the ancestor seems like it ought to be irrelevant to the child checkbox's behavior.
No, not really. The event bubbles, and all handlers (including the ones on ancestors) may affect it.
It seems as if, for the checkbox change to occur, the click event needs to bubble freely all the way to the document root.
Not exactly. It doesn't need to arrive at the document root, but it must not have been default-prevented.
You might want to read the architecture notes in the DOM spec on event flow, default actions and cancelable events.
So what does happen step-by-step?
- You click on the checkbox
- It gets checked
- The event is dispatched on the document root
- (Not in IE): capture phase, nothing happens with your handlers
- The event arrives at the
<input>
- …and begins to bubble
- On the
<div>
, it is handled. Your event listener calls thepreventDefault
method, setting an internalcancelled
flag. - It bubbles on, but nothing happens any more.
- Since the event was cancelled, the default action should not occur and the checkbox is reset to its previous state.
If you uncomment the second part of your code, steps 6+ look different:
- The event is handled on the
<input>
. Your listener calls thestopPropagation
method - …which leads to skipping the bubbling phase. (The div-listener will never be called)
- The default action was not prevented, and the checkbox stays checked.