As of 2024-12-31, the Stimulus reference for Actions says:
When an element has more than one action for the same event, Stimulus invokes the actions from left to right in the order that their descriptors appear.
This isn't entirely accurate. If you examine the dispatcher, a separate event listener is registered for each unique sequence of options (presumably to support the options to addEventListener):
|
const cacheKey = this.cacheKey(eventName, eventOptions) |
|
let eventListener = eventListenerMap.get(cacheKey) |
|
if (!eventListener) { |
|
eventListener = this.createEventListener(eventTarget, eventName, eventOptions) |
|
eventListenerMap.set(cacheKey, eventListener) |
|
} |
(where cacheKey is defined like so:)
|
private cacheKey(eventName: string, eventOptions: any): string { |
|
const parts = [eventName] |
|
Object.keys(eventOptions) |
|
.sort() |
|
.forEach((key) => { |
|
parts.push(`${eventOptions[key] ? "" : "!"}${key}`) |
|
}) |
|
return parts.join(":") |
|
} |
Only bindings within a single event listener will be ordered; everything else is undefined:
|
get bindings(): Binding[] { |
|
return Array.from(this.unorderedBindings).sort((left, right) => { |
|
const leftIndex = left.index, |
|
rightIndex = right.index |
|
return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0 |
|
}) |
|
} |
This may be the expected for capture, once, and passive, but I would not have expected this for stop, prevent, self, or my custom options.
I suggest two possible resolutions:
- Rework the internals of dispatcher so that only
capture, once, and passive are part of the cache key. IMO this matches what most users would expect of the library.
- Document this behavior without changing the code. I would find this unfortunate and reduces the utility of the action options, but is definitely less work and has less compatibility implications.
As of 2024-12-31, the Stimulus reference for Actions says:
This isn't entirely accurate. If you examine the dispatcher, a separate event listener is registered for each unique sequence of options (presumably to support the options to
addEventListener):stimulus/src/core/dispatcher.ts
Lines 83 to 88 in 8cbca6d
(where
cacheKeyis defined like so:)stimulus/src/core/dispatcher.ts
Lines 113 to 121 in 8cbca6d
Only bindings within a single event listener will be ordered; everything else is undefined:
stimulus/src/core/event_listener.ts
Lines 50 to 56 in 8cbca6d
This may be the expected for
capture,once, andpassive, but I would not have expected this forstop,prevent,self, or my custom options.I suggest two possible resolutions:
capture,once, andpassiveare part of the cache key. IMO this matches what most users would expect of the library.