Skip to content

Commit 0f94c89

Browse files
authored
[feat] dispatch cancelable custom events (#7064)
1 parent c371c3f commit 0f94c89

File tree

5 files changed

+43
-10
lines changed

5 files changed

+43
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Return the context object in `setContext` [#7427](https://github.com/sveltejs/svelte/issues/7427)
66
* Fix `{@const}` tag not working inside Component when there's no `let:` [#7189](https://github.com/sveltejs/svelte/issues/7189)
77
* Ignore comments in `{#each}` blocks when containing elements with `animate:` ([#3999](https://github.com/sveltejs/svelte/issues/3999))
8+
* Add a third parameter to the returned function of `createEventDispatcher` that allows passing an object of `{ cancelable: true }` to create a cancelable custom event. The returned function when called will also return a boolean depending on whether the event is cancelled ([#7064](https://github.com/sveltejs/svelte/pull/7064))
89

910
## 3.47.0
1011

site/content/docs/03-run-time.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,14 @@ Retrieves the whole context map that belongs to the closest parent component. Mu
221221
#### `createEventDispatcher`
222222

223223
```js
224-
dispatch: ((name: string, detail?: any) => void) = createEventDispatcher();
224+
dispatch: ((name: string, detail?: any, options?: DispatchOptions) => boolean) = createEventDispatcher();
225225
```
226226

227227
---
228228

229229
Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). Event dispatchers are functions that can take two arguments: `name` and `detail`.
230230

231-
Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) and are not cancellable with `event.preventDefault()`. The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data.
231+
Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data.
232232

233233
```sv
234234
<script>
@@ -254,6 +254,27 @@ Events dispatched from child components can be listened to in their parent. Any
254254
<Child on:notify="{callbackFunction}"/>
255255
```
256256

257+
---
258+
259+
Events can be cancelable by passing a third parameter to the dispatch function. The function returns `false` if the event is cancelled with `event.preventDefault()`, otherwise it returns `true`.
260+
261+
```sv
262+
<script>
263+
import { createEventDispatcher } from 'svelte';
264+
265+
const dispatch = createEventDispatcher();
266+
267+
function notify() {
268+
const shouldContinue = dispatch('notify', 'detail value', { cancelable: true });
269+
if (shouldContinue) {
270+
// no one called preventDefault
271+
} else {
272+
// a listener called preventDefault
273+
}
274+
}
275+
</script>
276+
```
277+
257278
### `svelte/store`
258279

259280
The `svelte/store` module exports functions for creating [readable](/docs#run-time-svelte-store-readable), [writable](/docs#run-time-svelte-store-writable) and [derived](/docs#run-time-svelte-store-derived) stores.

src/runtime/internal/dev.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { custom_event, append, append_hydration, insert, insert_hydration, detac
22
import { SvelteComponent } from './Component';
33

44
export function dispatch_dev<T=any>(type: string, detail?: T) {
5-
document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }, true));
5+
document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }, { bubbles: true }));
66
}
77

88
export function append_dev(target: Node, node: Node) {

src/runtime/internal/dom.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,9 +634,9 @@ export function toggle_class(element, name, toggle) {
634634
element.classList[toggle ? 'add' : 'remove'](name);
635635
}
636636

637-
export function custom_event<T=any>(type: string, detail?: T, bubbles: boolean = false) {
637+
export function custom_event<T=any>(type: string, detail?: T, { bubbles = false, cancelable = false } = {}): CustomEvent<T> {
638638
const e: CustomEvent<T> = document.createEvent('CustomEvent');
639-
e.initCustomEvent(type, bubbles, false, detail);
639+
e.initCustomEvent(type, bubbles, cancelable, detail);
640640
return e;
641641
}
642642

src/runtime/internal/lifecycle.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,33 @@ export function onDestroy(fn: () => any) {
2727
get_current_component().$$.on_destroy.push(fn);
2828
}
2929

30-
export function createEventDispatcher<
31-
EventMap extends {} = any
32-
>(): <EventKey extends Extract<keyof EventMap, string>>(type: EventKey, detail?: EventMap[EventKey]) => void {
30+
export interface DispatchOptions {
31+
cancelable?: boolean;
32+
}
33+
34+
export function createEventDispatcher<EventMap extends {} = any>(): <
35+
EventKey extends Extract<keyof EventMap, string>
36+
>(
37+
type: EventKey,
38+
detail?: EventMap[EventKey],
39+
options?: DispatchOptions
40+
) => boolean {
3341
const component = get_current_component();
3442

35-
return (type: string, detail?: any) => {
43+
return (type: string, detail?: any, { cancelable = false } = {}): boolean => {
3644
const callbacks = component.$$.callbacks[type];
3745

3846
if (callbacks) {
3947
// TODO are there situations where events could be dispatched
4048
// in a server (non-DOM) environment?
41-
const event = custom_event(type, detail);
49+
const event = custom_event(type, detail, { cancelable });
4250
callbacks.slice().forEach(fn => {
4351
fn.call(component, event);
4452
});
53+
return !event.defaultPrevented;
4554
}
55+
56+
return true;
4657
};
4758
}
4859

0 commit comments

Comments
 (0)