-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Svelte 5: Event delegation thoughts #9714
Comments
I wrote the original issue where that MR came out. I don't know how Is it possible that svelte exposes those new events modifier functions and use them internally when <script>
// Hypotetical new export
import { preventDefault } from 'svelte/events'
function handler() {}
</script>
<!-- This event handler -->
<button on:click|preventDefault={handler}>Click me</button>
<!-- would be read and transformed as this -->
<button onclick={preventDefault(handler)}>Click me</button> By doing that I hope we could completely get rid off the logic to handle The issue with that is I don't really know how we could have event modifier functions that change the listening behavior of the event (capture or bubble) or even changing the |
@chainlist I've made this package to explore how event handling could be supported in Svelte 5 + provide the modifier wrapper functions https://github.com/Not-Jayden/svelte-event Still early stages and agree it would be nice if svelte provided them. Also not sure if any of this is at all related to what Simon is discussing tbh haha. |
Related: #9777 |
So what are the cases where we are mixing delegated events and non-delegated events of the same type? Surely Svelte should only be applying one kind here? If the user is adding their own custom event handlers, then we could improve interop like the event system does on React, but maybe we first need to understand all the cases. |
I'm not sure whether this happens for event handlers that Svelte manages, but one scenario that I've run into is when adding my own event handlers. For example:
As an example, if you run this component and click the button, the logs will be in the "wrong" order: <script>
let i = 0;
let defined = false;
if (!defined) {
defined = true;
customElements.define("web-component", class extends HTMLElement {
constructor() {
super();
this.addEventListener("pointerdown", this);
}
handleEvent() {
console.log("web component", ++i);
}
});
}
function action(node) {
node.addEventListener("pointerdown", () => console.log("action", ++i));
}
</script>
<web-component>
<div use:action>
<button on:pointerdown={() => console.log("event handler", ++i)}>hello!</button>
</div>
</web-component> That is, you'll see
rather than
I appreciate why event delegation is the default behavior, but maybe it's worth adding an additional modifier to bind event handlers directly? So I could opt in via something like this: <button on:pointerdown|direct={() => console.log("event handler", ++i)}>hello!</button> |
Others also have problems with edge cases: solidjs/solid#1786 |
Only delegate event attributes, and don't take into account bindings/actions while determining that. Never delegate `on:` directives. This simplifies the logic and makes it easier to explain, while avoiding any accidental breaking changes when upgrading from Svelte 4 to 5 without changing code. Fixes #10165 Related to #9714
Only delegate event attributes, and don't take into account bindings/actions while determining that. Never delegate `on:` directives. This simplifies the logic and makes it easier to explain, while avoiding any accidental breaking changes when upgrading from Svelte 4 to 5 without changing code. Fixes #10165 Related to #9714
* fix: simplify event delegation logic Only delegate event attributes, and don't take into account bindings/actions while determining that. Never delegate `on:` directives. This simplifies the logic and makes it easier to explain, while avoiding any accidental breaking changes when upgrading from Svelte 4 to 5 without changing code. Fixes #10165 Related to #9714 * update types
I have event listener in the container action. Right now calling <script>
function buttonHandler(e) {
e.stopPropagation()
console.log('clicked button')
}
const action = (node) => {
node.addEventListener('click', () => {
console.log('Clicked action')
})
return {}
}
</script>
<div use:action>
Container with action
<button onclick={buttonHandler}>
Button
</button>
</div> |
@minht11 This is to be expected. If you change the button to be |
This is what I've been doing: svelte 5 dev link The demo link has event.stopPropagation in the listener action, no reason there can't be moved to the module scope in an export function or added as an additional prop in the listener. E.g. |
Since #11295 mixing new and old event systems is forbidden. Any solution to my use case in #9714 (comment)? |
Running to the same problem as @minht11 did with stopPropagation not working for event listeners registered on ancestor element via Please find below for a example of working code in Svelte 4 and the equivalent but not working in Svelte 5. Happy to create a dedicated issue if necessary. As of Svelte 4.2.17See REPL here <script>
import { onMount } from 'svelte';
let count = 0;
function increment(e) {
e.stopPropagation();
count += 1;
}
let sectionEl
onMount(() => {
sectionEl.addEventListener('click', (e) => {
console.log('logged from addEventListener');
});
});
</script>
<section bind:this={sectionEl} on:click={() => console.log('logged from on:click')}>
<button on:click={increment}>
clicks: {count}
</button>
</section> Clicking on As of Svelte 5.0.0-next.136See REPL here <script>
let count = $state(0);
function increment(e) {
e.stopPropagation();
count += 1;
}
let sectionEl
$effect(() => {
sectionEl.addEventListener('click', () => {
console.log('logged from addEventListener');
});
});
</script>
<section bind:this={sectionEl} onclick={() => console.log('logged from onclick')}>
<button onclick={increment}>
clicks: {count}
</button>
</section> Clicking on Workaround
- <button onclick={increment}>
+ <button onclickcapture={increment}> |
#11912 should help here |
Closing since the caveats are documented and we have |
I ran into this problem too. |
Describe the problem
#9696 uncovered a major flaw in our event delegation logic: If an event is stopped from being propagated, and that event is delegated, then the propagation isn't really stopped. If there's now another event handler for the same event type above that delegated listener but below the root that listens to the delegated events, and that event handler is not delegated, then it will still get notified of the event.
Describe the proposed solution
I'm honestly not sure if we can fix this in a sensible way that covers all cases, at least not as long as we have
on:x
still around.Alternatives considered
Live with the edge cases of this sometimes not working correctly
Importance
would make my life easier
The text was updated successfully, but these errors were encountered: