-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
Describe the bug
The unmount logic removes handlers from the target element, but if the target element is e.g. the document.body this can lead to event delegation no longer working properly.
For document listeners there is some logic that checks whether the listener is still needed but there is no check for the target.
svelte/packages/svelte/src/internal/client/render.js
Lines 250 to 262 in 2eb54f5
| return () => { | |
| for (var event_name of registered_events) { | |
| target.removeEventListener(event_name, handle_event_propagation); | |
| var n = /** @type {number} */ (document_listeners.get(event_name)); | |
| if (--n === 0) { | |
| document.removeEventListener(event_name, handle_event_propagation); | |
| document_listeners.delete(event_name); | |
| } else { | |
| document_listeners.set(event_name, n); | |
| } | |
| } |
Still, some fairly specific conditions need to be met for this bug to occur, e.g. with SvelteKit the default wrapper div in app.html seems to cause handlers to be attached there instead of on the body, so the issue disappears.
The component with the handler also needed a forwarding instruction (e.g. on:click in my case), otherwise the event handler did not exist before the mount. (I observed this issue with carbon-components-svelte, which ships in syntax for Svelte 3/4 compatibility, hence it has a lot of manual event forwarding.)
Reproduction
Standalone code/steps
<script>
import { mount, unmount } from 'svelte';
import Dialog from './dialog.svelte';
import Button from './button.svelte';
function onClick() {
const dialog = mount(Dialog, {
target: document.body,
props: {},
events: { close() { unmount(dialog); } },
});
}
</script>
<Button onclick={onClick}>
Direct Text
<div>Child</div>
</Button><!-- button.svelte -->
<button {...$$restProps} on:click>
<slot></slot>
</button><!-- dialog.svelte -->
<dialog {@attach node => node.showModal()} on:close>
<form method="dialog">
<button>Close</button>
<p>Dialog</p>
</form>
</dialog>Steps:
- Click button to open dialog
- Close dialog
- Click button again:
- Hitting the child element in the button does not open the dialog
- Hitting the direct text/padding opens the dialog
Logs
System Info
REPL ?version=5.47.1Severity
annoyance