Is it possible to dispatch a svelte event (created with createEventDispatcher
) with a target object like a native browser event?
I.e. receiving on the handler side event.target.value
instead of event.detail.
Yes, it's possible but involve some hacking.
You can take a look into the svelte source code to see how event dispatching works. I'll outline the key parts as below.
Let's say we have two components, Inner
and Outer
.
<!-- Inner.svelte -->
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
const dispatchFoo = () => dispatch('foo', 'bar')
</script>
<button on:click={dispatchFoo}>send</button>
<!-- Outer.svelte -->
<script>
import Inner from './Inner.svelte'
const handleFoo = (e) => { console.log('receive foo', e) }
</script>
<Inner on:foo={handleFoo}></Inner>
If you think about it, the event handler handleFoo
is created in Outer
, but it's actually register on Inner
.
Check the compiled JS code you'll see:
inner.$$.callbacks["foo"]
holds the event handler [src]dispatch
, it just read the inner.$$.callbacks["foo"]
for potential handlers, and if found, call them with CustomEvent
as argument [src]The only way to set customEvent.target
is by dispatch that custom event using element.dispatchEvent(customEvent)
. But element.dispatchEvent
is not used through the whole process.
Write your own createEventDispatcher
.
import { get_current_component } from 'svelte/internal'
function createEventDispatcher() {
const component = get_current_component();
return (type, target, detail) => {
const callbacks = component.$$.callbacks[type];
if (callbacks) {
const event = new CustomEvent(type, { detail });
// the key is to call `dispatchEvent` manually to set `event.target`
target.dispatchEvent(event);
callbacks.slice().forEach((fn) => {
fn.call(component, event);
});
}
};
}