Skip to content

Commit be02b7e

Browse files
authored
event_context (#13737)
1 parent fb052be commit be02b7e

File tree

6 files changed

+69
-38
lines changed

6 files changed

+69
-38
lines changed

.changeset/giant-plants-shout.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: ensure event context is reset before invoking callback

packages/svelte/src/internal/client/dom/elements/events.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import { hydrating } from '../hydration.js';
55
import { queue_micro_task } from '../task.js';
66
import { FILENAME } from '../../../../constants.js';
77
import * as w from '../../warnings.js';
8+
import {
9+
active_effect,
10+
active_reaction,
11+
set_active_effect,
12+
set_active_reaction
13+
} from '../../runtime.js';
814

915
/** @type {Set<string>} */
1016
export const all_registered_events = new Set();
@@ -55,7 +61,17 @@ export function create_event(event_name, dom, handler, options) {
5561
handle_event_propagation.call(dom, event);
5662
}
5763
if (!event.cancelBubble) {
58-
return handler.call(this, event);
64+
var previous_reaction = active_reaction;
65+
var previous_effect = active_effect;
66+
67+
set_active_reaction(null);
68+
set_active_effect(null);
69+
try {
70+
return handler.call(this, event);
71+
} finally {
72+
set_active_reaction(previous_reaction);
73+
set_active_effect(previous_effect);
74+
}
5975
}
6076
}
6177

@@ -196,6 +212,16 @@ export function handle_event_propagation(event) {
196212
}
197213
});
198214

215+
// This started because of Chromium issue https://chromestatus.com/feature/5128696823545856,
216+
// where removal or moving of of the DOM can cause sync `blur` events to fire, which can cause logic
217+
// to run inside the current `active_reaction`, which isn't what we want at all. However, on reflection,
218+
// it's probably best that all event handled by Svelte have this behaviour, as we don't really want
219+
// an event handler to run in the context of another reaction or effect.
220+
var previous_reaction = active_reaction;
221+
var previous_effect = active_effect;
222+
set_active_reaction(null);
223+
set_active_effect(null);
224+
199225
try {
200226
/**
201227
* @type {unknown}
@@ -253,6 +279,8 @@ export function handle_event_propagation(event) {
253279
event.__root = handler_element;
254280
// @ts-ignore remove proxy on currentTarget
255281
delete event.currentTarget;
282+
set_active_reaction(previous_reaction);
283+
set_active_effect(previous_effect);
256284
}
257285
}
258286

packages/svelte/src/internal/client/dom/elements/transitions.js

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
/** @import { AnimateFn, Animation, AnimationConfig, EachItem, Effect, TransitionFn, TransitionManager } from '#client' */
22
import { noop, is_function } from '../../../shared/utils.js';
33
import { effect } from '../../reactivity/effects.js';
4-
import {
5-
active_effect,
6-
active_reaction,
7-
set_active_effect,
8-
set_active_reaction,
9-
untrack
10-
} from '../../runtime.js';
4+
import { active_effect, untrack } from '../../runtime.js';
115
import { loop } from '../../loop.js';
126
import { should_intro } from '../../render.js';
137
import { current_each_item } from '../blocks/each.js';
@@ -21,16 +15,7 @@ import { queue_micro_task } from '../task.js';
2115
* @returns {void}
2216
*/
2317
function dispatch_event(element, type) {
24-
var previous_reaction = active_reaction;
25-
var previous_effect = active_effect;
26-
set_active_reaction(null);
27-
set_active_effect(null);
28-
try {
29-
element.dispatchEvent(new CustomEvent(type));
30-
} finally {
31-
set_active_reaction(previous_reaction);
32-
set_active_effect(previous_effect);
33-
}
18+
element.dispatchEvent(new CustomEvent(type));
3419
}
3520

3621
/**

packages/svelte/src/internal/client/reactivity/effects.js

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import {
1515
set_is_destroying_effect,
1616
set_is_flushing_effect,
1717
set_signal_status,
18-
untrack,
19-
set_active_effect
18+
untrack
2019
} from '../runtime.js';
2120
import {
2221
DIRTY,
@@ -423,26 +422,13 @@ export function destroy_effect(effect, remove_dom = true) {
423422
/** @type {TemplateNode | null} */
424423
var node = effect.nodes_start;
425424
var end = effect.nodes_end;
426-
var previous_reaction = active_reaction;
427-
var previous_effect = active_effect;
428425

429-
// Really we only need to do this in Chromium because of https://chromestatus.com/feature/5128696823545856,
430-
// as removal of the DOM can cause sync `blur` events to fire, which can cause logic to run inside
431-
// the current `active_reaction`, which isn't what we want at all. Additionally, the blur event handler
432-
// might create a derived or effect and they will be incorrectly attached to the wrong thing
433-
set_active_reaction(null);
434-
set_active_effect(null);
435-
try {
436-
while (node !== null) {
437-
/** @type {TemplateNode | null} */
438-
var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
426+
while (node !== null) {
427+
/** @type {TemplateNode | null} */
428+
var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
439429

440-
node.remove();
441-
node = next;
442-
}
443-
} finally {
444-
set_active_reaction(previous_reaction);
445-
set_active_effect(previous_effect);
430+
node.remove();
431+
node = next;
446432
}
447433

448434
removed = true;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
test({ assert, target, logs }) {
6+
const [b1] = target.querySelectorAll('button');
7+
8+
b1?.click();
9+
b1?.click();
10+
b1?.click();
11+
flushSync();
12+
assert.htmlEqual(target.innerHTML, '<button>4</button>');
13+
}
14+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
let count = $state(0);
3+
let button = $state();
4+
5+
function do_thing() {
6+
button?.click();
7+
return false;
8+
}
9+
</script>
10+
11+
<button bind:this={button} onclick={() => count++ }>{count}</button>
12+
13+
{#if do_thing()}{/if}

0 commit comments

Comments
 (0)