event_context (#13737)

pull/13744/head
Dominic Gannaway 11 months ago committed by GitHub
parent fb052be96e
commit be02b7e54c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure event context is reset before invoking callback

@ -5,6 +5,12 @@ import { hydrating } from '../hydration.js';
import { queue_micro_task } from '../task.js'; import { queue_micro_task } from '../task.js';
import { FILENAME } from '../../../../constants.js'; import { FILENAME } from '../../../../constants.js';
import * as w from '../../warnings.js'; import * as w from '../../warnings.js';
import {
active_effect,
active_reaction,
set_active_effect,
set_active_reaction
} from '../../runtime.js';
/** @type {Set<string>} */ /** @type {Set<string>} */
export const all_registered_events = new Set(); export const all_registered_events = new Set();
@ -55,7 +61,17 @@ export function create_event(event_name, dom, handler, options) {
handle_event_propagation.call(dom, event); handle_event_propagation.call(dom, event);
} }
if (!event.cancelBubble) { if (!event.cancelBubble) {
var previous_reaction = active_reaction;
var previous_effect = active_effect;
set_active_reaction(null);
set_active_effect(null);
try {
return handler.call(this, event); return handler.call(this, event);
} finally {
set_active_reaction(previous_reaction);
set_active_effect(previous_effect);
}
} }
} }
@ -196,6 +212,16 @@ export function handle_event_propagation(event) {
} }
}); });
// This started because of Chromium issue https://chromestatus.com/feature/5128696823545856,
// where removal or moving of of the DOM can cause sync `blur` events to fire, which can cause logic
// to run inside the current `active_reaction`, which isn't what we want at all. However, on reflection,
// it's probably best that all event handled by Svelte have this behaviour, as we don't really want
// an event handler to run in the context of another reaction or effect.
var previous_reaction = active_reaction;
var previous_effect = active_effect;
set_active_reaction(null);
set_active_effect(null);
try { try {
/** /**
* @type {unknown} * @type {unknown}
@ -253,6 +279,8 @@ export function handle_event_propagation(event) {
event.__root = handler_element; event.__root = handler_element;
// @ts-ignore remove proxy on currentTarget // @ts-ignore remove proxy on currentTarget
delete event.currentTarget; delete event.currentTarget;
set_active_reaction(previous_reaction);
set_active_effect(previous_effect);
} }
} }

@ -1,13 +1,7 @@
/** @import { AnimateFn, Animation, AnimationConfig, EachItem, Effect, TransitionFn, TransitionManager } from '#client' */ /** @import { AnimateFn, Animation, AnimationConfig, EachItem, Effect, TransitionFn, TransitionManager } from '#client' */
import { noop, is_function } from '../../../shared/utils.js'; import { noop, is_function } from '../../../shared/utils.js';
import { effect } from '../../reactivity/effects.js'; import { effect } from '../../reactivity/effects.js';
import { import { active_effect, untrack } from '../../runtime.js';
active_effect,
active_reaction,
set_active_effect,
set_active_reaction,
untrack
} from '../../runtime.js';
import { loop } from '../../loop.js'; import { loop } from '../../loop.js';
import { should_intro } from '../../render.js'; import { should_intro } from '../../render.js';
import { current_each_item } from '../blocks/each.js'; import { current_each_item } from '../blocks/each.js';
@ -21,16 +15,7 @@ import { queue_micro_task } from '../task.js';
* @returns {void} * @returns {void}
*/ */
function dispatch_event(element, type) { function dispatch_event(element, type) {
var previous_reaction = active_reaction;
var previous_effect = active_effect;
set_active_reaction(null);
set_active_effect(null);
try {
element.dispatchEvent(new CustomEvent(type)); element.dispatchEvent(new CustomEvent(type));
} finally {
set_active_reaction(previous_reaction);
set_active_effect(previous_effect);
}
} }
/** /**

@ -15,8 +15,7 @@ import {
set_is_destroying_effect, set_is_destroying_effect,
set_is_flushing_effect, set_is_flushing_effect,
set_signal_status, set_signal_status,
untrack, untrack
set_active_effect
} from '../runtime.js'; } from '../runtime.js';
import { import {
DIRTY, DIRTY,
@ -423,16 +422,7 @@ export function destroy_effect(effect, remove_dom = true) {
/** @type {TemplateNode | null} */ /** @type {TemplateNode | null} */
var node = effect.nodes_start; var node = effect.nodes_start;
var end = effect.nodes_end; var end = effect.nodes_end;
var previous_reaction = active_reaction;
var previous_effect = active_effect;
// Really we only need to do this in Chromium because of https://chromestatus.com/feature/5128696823545856,
// as removal of the DOM can cause sync `blur` events to fire, which can cause logic to run inside
// the current `active_reaction`, which isn't what we want at all. Additionally, the blur event handler
// might create a derived or effect and they will be incorrectly attached to the wrong thing
set_active_reaction(null);
set_active_effect(null);
try {
while (node !== null) { while (node !== null) {
/** @type {TemplateNode | null} */ /** @type {TemplateNode | null} */
var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node)); var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
@ -440,10 +430,6 @@ export function destroy_effect(effect, remove_dom = true) {
node.remove(); node.remove();
node = next; node = next;
} }
} finally {
set_active_reaction(previous_reaction);
set_active_effect(previous_effect);
}
removed = true; removed = true;
} }

@ -0,0 +1,14 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
test({ assert, target, logs }) {
const [b1] = target.querySelectorAll('button');
b1?.click();
b1?.click();
b1?.click();
flushSync();
assert.htmlEqual(target.innerHTML, '<button>4</button>');
}
});

@ -0,0 +1,13 @@
<script>
let count = $state(0);
let button = $state();
function do_thing() {
button?.click();
return false;
}
</script>
<button bind:this={button} onclick={() => count++ }>{count}</button>
{#if do_thing()}{/if}
Loading…
Cancel
Save