For one DOM event, capture listeners run from the outer ancestors down toward target. At target, capture listeners registered on that node run before its bubble listeners, although both observations are in the target phase. If the event supports bubbling, bubble listeners then run from target back through the ancestors. Reading the observations in this order reveals the first listener that could have changed or stopped the route.
target identifies where the event began as exposed to the listener, while currentTarget identifies the node whose listener is running at that observation. composedPath() supplies the ordered node route visible from the current context; in this trace it is shown from target toward the root. Read it in reverse for capture and forward for bubble. Around Shadow DOM, the visible target and path may be retargeted or omit closed-shadow internals, so use the recorded path instead of assuming the light-DOM parent chain.
stopPropagation prevents the event from continuing to later nodes. Other listeners in the same capture or bubble pass on the currentTarget can still run, but calling it from a capture listener on target skips the later target bubble pass. stopImmediatePropagation also blocks the remaining listeners in the current pass on that node. A one-shot probe records the method, phase, and currentTarget where the stop was called, so compare that stop point with the last observation instead of guessing from registration code alone. After changing the handler, capture the same event again to confirm that the route now reaches the expected ancestor.
Common questions and answers about this topic.
Capture listeners run from the outer ancestors toward target. On target, capture listeners run before bubble listeners, with both reported in the target phase. If bubbles is true, bubble listeners then run from target back through the ancestors. Listener registration order decides the order between listeners of the same type on the same node.
First check event.bubbles: some event types do not bubble. If it is true, look for stopPropagation or stopImmediatePropagation before the parent, confirm that the parent is actually on composedPath(), and check whether a Shadow DOM or framework boundary changes the visible route. A trace distinguishes these cases by showing the path, the last observation, and any recorded stop call.
event.target is the event origin exposed to the listener and usually stays the same as propagation continues. event.currentTarget is the node whose listener is currently running, so it changes at each observation. In delegated handlers, use target to identify the clicked descendant and currentTarget to identify the element that owns the handler.
Not always. Other listeners in the same capture or bubble pass on the currentTarget can still run. However, if a capture listener on target calls stopPropagation, the later target bubble pass is skipped. stopImmediatePropagation also blocks the remaining listeners in the current pass as well as further propagation. The probe records which method was called so these cases are not confused.
Choose the event type, copy the one-shot probe, and run it in DevTools on the affected page. Reproduce the event once, then paste the generated JSON trace into this tool. The probe removes its listeners and restores patched methods after the capture, records no more than that one event, and does not send the trace over the network.
Event propagation describes where one DOM event travels and which capture, target, and bubble listeners can run along its node path. The event loop describes when JavaScript work runs by coordinating the call stack, microtasks, and macrotasks. A click enters the event loop as a task; once the event's dispatch begins, propagation determines the listener order within that event.