From c6af26ffd167d4480b139801cc6964ffe7392556 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 Oct 2024 08:42:49 -0400 Subject: [PATCH] fix: add media listeners immediately when using `bind:paused` (#13502) Fix a bug where play/pause events may never be added to the media element. Also simplifies the logic by making it an effect instead of a render effect --------- Co-authored-by: Simon Holthausen --- .changeset/fair-items-bathe.md | 5 ++ .../client/dom/elements/bindings/media.js | 54 +++++-------------- 2 files changed, 18 insertions(+), 41 deletions(-) create mode 100644 .changeset/fair-items-bathe.md diff --git a/.changeset/fair-items-bathe.md b/.changeset/fair-items-bathe.md new file mode 100644 index 0000000000..3abaf8e7bd --- /dev/null +++ b/.changeset/fair-items-bathe.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add media listeners immediately when using `bind:paused` diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/media.js b/packages/svelte/src/internal/client/dom/elements/bindings/media.js index ad955c89dc..444a1b3439 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/media.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/media.js @@ -138,56 +138,28 @@ export function bind_playback_rate(media, get, set = get) { * @param {(paused: boolean) => void} set */ export function bind_paused(media, get, set = get) { - var mounted = hydrating; var paused = get(); - var callback = () => { + var update = () => { if (paused !== media.paused) { - paused = media.paused; set((paused = media.paused)); } }; - if (paused == null) { - callback(); - } - - // Defer listening if not mounted yet so that the first canplay event doesn't cause a potentially wrong update - if (mounted) { - // If someone switches the src while media is playing, the player will pause. - // Listen to the canplay event to get notified of this situation. - listen(media, ['play', 'pause', 'canplay'], callback, false); - } + // If someone switches the src while media is playing, the player will pause. + // Listen to the canplay event to get notified of this situation. + listen(media, ['play', 'pause', 'canplay'], update, paused == null); - render_effect(() => { - paused = !!get(); - - if (paused !== media.paused) { - var toggle = () => { - mounted = true; - if (paused) { - media.pause(); - } else { - media.play().catch(() => { - set((paused = true)); - }); - } - }; - - if (mounted) { - toggle(); + // Needs to be an effect to ensure media element is mounted: else, if paused is `false` (i.e. should play right away) + // a "The play() request was interrupted by a new load request" error would be thrown because the resource isn't loaded yet. + effect(() => { + if ((paused = !!get()) !== media.paused) { + if (paused) { + media.pause(); } else { - // If this is the first invocation in dom mode, the media element isn't mounted yet, - // and therefore its resource isn't loaded yet. We need to wait for the canplay event - // in this case or else we'll get a "The play() request was interrupted by a new load request" error. - media.addEventListener( - 'canplay', - () => { - listen(media, ['play', 'pause', 'canplay'], callback, false); - toggle(); - }, - { once: true } - ); + media.play().catch(() => { + set((paused = true)); + }); } } });