From a5fd28a14bcdce10d8b9fcee1d323d5b8788cbc6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 20 May 2024 22:05:39 +0100 Subject: [PATCH] fix: inline pointer events now correctly work in Chrome (#11695) * fix: inline pointer events now correctly work in Chrome * fix more --- .changeset/quiet-cobras-smile.md | 5 +++ .../client/dom/elements/attributes.js | 10 +++-- .../internal/client/dom/elements/events.js | 38 +++++++++++++++---- 3 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 .changeset/quiet-cobras-smile.md diff --git a/.changeset/quiet-cobras-smile.md b/.changeset/quiet-cobras-smile.md new file mode 100644 index 000000000..10b487983 --- /dev/null +++ b/.changeset/quiet-cobras-smile.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: inline pointer events now correctly work in Chrome diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 399c0ee9a..c81c3c2d5 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -2,7 +2,7 @@ import { DEV } from 'esm-env'; import { hydrating } from '../hydration.js'; import { get_descriptors, get_prototype_of, map_get, map_set } from '../../utils.js'; import { AttributeAliases, DelegatedEvents, namespace_svg } from '../../../../constants.js'; -import { delegate } from './events.js'; +import { create_event, delegate } from './events.js'; import { autofocus } from './misc.js'; import { effect, effect_root } from '../../reactivity/effects.js'; import * as w from '../../warnings.js'; @@ -151,9 +151,13 @@ export function set_attributes(element, prev, next, lowercase_attributes, css_ha if (!delegated) { // we use `addEventListener` here because these events are not delegated if (!prev) { - events.push([key, value, () => element.addEventListener(event_name, value, opts)]); + events.push([ + key, + value, + () => (next[key] = create_event(event_name, element, value, opts)) + ]); } else { - element.addEventListener(event_name, value, opts); + next[key] = create_event(event_name, element, value, opts); } } else { // @ts-ignore diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index e0298af92..fdce8de14 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -2,6 +2,7 @@ import { render_effect } from '../../reactivity/effects.js'; import { all_registered_events, root_event_handles } from '../../render.js'; import { define_property, is_array } from '../../utils.js'; import { hydrating } from '../hydration.js'; +import { queue_task } from '../task.js'; /** * SSR adds onload and onerror attributes to catch those events before the hydration. @@ -34,18 +35,14 @@ export function replay_events(dom) { * @param {string} event_name * @param {Element} dom * @param {EventListener} handler - * @param {boolean} capture - * @param {boolean} [passive] - * @returns {void} + * @param {AddEventListenerOptions} options */ -export function event(event_name, dom, handler, capture, passive) { - var options = { capture, passive }; - +export function create_event(event_name, dom, handler, options) { /** * @this {EventTarget} */ function target_handler(/** @type {Event} */ event) { - if (!capture) { + if (!options.capture) { // Only call in the bubble phase, else delegated events would be called before the capturing events handle_event_propagation(dom, event); } @@ -54,7 +51,32 @@ export function event(event_name, dom, handler, capture, passive) { } } - dom.addEventListener(event_name, target_handler, options); + // Chrome has a bug where pointer events don't work when attached to a DOM element that has been cloned + // with cloneNode() and the DOM element is disconnected from the document. To ensure the event works, we + // defer the attachment till after it's been appended to the document. TODO: remove this once Chrome fixes + // this bug. + if (event_name.startsWith('pointer')) { + queue_task(() => { + dom.addEventListener(event_name, target_handler, options); + }); + } else { + dom.addEventListener(event_name, target_handler, options); + } + + return target_handler; +} + +/** + * @param {string} event_name + * @param {Element} dom + * @param {EventListener} handler + * @param {boolean} capture + * @param {boolean} [passive] + * @returns {void} + */ +export function event(event_name, dom, handler, capture, passive) { + var options = { capture, passive }; + var target_handler = create_event(event_name, dom, handler, options); // @ts-ignore if (dom === document.body || dom === window || dom === document) {