diff --git a/packages/alpinejs/src/utils/dispatch.js b/packages/alpinejs/src/utils/dispatch.js
index 26f198aef..297b74862 100644
--- a/packages/alpinejs/src/utils/dispatch.js
+++ b/packages/alpinejs/src/utils/dispatch.js
@@ -1,12 +1,14 @@
-export function dispatch(el, name, detail = {}) {
- el.dispatchEvent(
+export function dispatch(el, name, detail = {}, options = {}) {
+ return el.dispatchEvent(
new CustomEvent(name, {
detail,
bubbles: true,
// Allows events to pass the shadow DOM barrier.
composed: true,
cancelable: true,
+ // Allows overriding the default event options.
+ ...options
})
)
}
diff --git a/packages/docs/src/en/magics/dispatch.md b/packages/docs/src/en/magics/dispatch.md
index e43f5ba04..9158ea59e 100644
--- a/packages/docs/src/en/magics/dispatch.md
+++ b/packages/docs/src/en/magics/dispatch.md
@@ -104,3 +104,42 @@ You can also use `$dispatch()` to trigger data updates for `x-model` data bindin
```
This opens up the door for making custom input components whose value can be set via `x-model`.
+
+
+## Cancelable events
+
+You can use the returned value of `$dispatch` to check if the event was canceled or not. This is useful when you want to prevent the default behavior of an action.
+
+```alpine
+
+
+
+
+
+
+
Hello
+
+
+
+```
+
+This could be useful when you want to prevent opening/closing a modal or something like that by using event handlers.
+
+
+## Overwriting options
+
+You can use the third parameter of `$dispatch` to overwrite the default options of the event. For example, you can set `bubbles` to `false`:
+
+```alpine
+
+
+
+
+
+
+
+
+
+```
+
+This is useful when you want to prevent the event from bubbling up to the parent elements.
diff --git a/tests/cypress/integration/magics/$dispatch.spec.js b/tests/cypress/integration/magics/$dispatch.spec.js
index b760be0b6..1c5107d26 100644
--- a/tests/cypress/integration/magics/$dispatch.spec.js
+++ b/tests/cypress/integration/magics/$dispatch.spec.js
@@ -14,3 +14,53 @@ test('$dispatch dispatches events properly',
get('span').should(haveText('baz'))
}
)
+
+test('$dispatch with bubbles overwrite to false shouldn\'t bubble (event handling on parent)',
+ html`
+
+
+
+
+
+ `,
+ ({ get }) => {
+ get('span').should(haveText('bar'))
+ get('button').click()
+ get('span').should(haveText('bar'))
+ }
+)
+
+test('$dispatch with bubbles overwrite to false shouldn\'t bubble (event handling on same element)',
+ html`
+
+
+
+
+
+ `,
+ ({ get }) => {
+ get('span').should(haveText('bar'))
+ get('button').click()
+ get('span').should(haveText('baz'))
+ }
+)
+
+test('$dispatch cancelable should be cancelable by $event.preventDefault()',
+ html`
+