This works without problem in Chrome and Firefox. Fails on Safari up to, and including Release 166 (Safari 16.4, WebKit 18616.1.6.11). The error in the console is "Unhandled Promise Rejection: AbortError: The operation was aborted." Test page: https://benfrain.com/playground/video-stop-start/index.html What should happen is that the videos pause as they are scrolled past and play when they are scrolled into view. This mechanism is achieved with this code: ``` function playPauseVideo() { let videos = document.querySelectorAll("video"); videos.forEach((video) => { // We can only control playback without insteraction if video is mute video.muted = true; // Play is a promise so we need to check we have it let playPromise = video.play(); if (playPromise !== undefined) { playPromise.then((_) => { let observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.intersectionRatio !== 1 && !video.paused) { video.pause(); } else if (video.paused) { video.play(); } }); }, { threshold: 0.2 } ); observer.observe(video); }); } }); } ``` I am unsure if this is a bug or a missing piece of requisite (undocumented) code that only Safari needs?
AFAIK, this is related to transitive user activation—I believe Firefox and Chrome allow video play to begin based on transitive user activation, whereas I think we're stricter about this. In Firefox and Chrome, the scroll event that triggers the intersection observer will allow a play for N seconds, thus by the time the intersection observer fires a play is allowed. In Safari, I believe we only allow a play directly from the scroll event, which thus means going via an intersection observer doesn't work. That said, I'm sure we tried to change this recently and then hit web compat problems by matching others.
Hi Sam, thanks for the prompt response. Given what you said, and this seeming like a pretty typical use case around playing media, what is the current 'Safari' way to achieve the same goal? Also, could you clarify; do you mean there is a chance Safari may eventually align itself with the other browsers in its execution, or that seems unlikely?
I don't think the problem is to do with user activation. I think it may be that playPauseVideo() is trying to play() the videos before they have loaded. Ben, can you try: window.onload = () => playPauseVideo();
(In reply to Marcos Caceres from comment #3) > I don't think the problem is to do with user activation. I think it may be > that playPauseVideo() is trying to play() the videos before they have > loaded. > > Ben, can you try: > window.onload = () => playPauseVideo(); Here is an alternative that checks if the video is not ready: function videoObsever(video) { return async (entries, observer) => { // guard against playing too early if (video.readyState < video.HAVE_CURRENT_DATA) { console.warn("video not ready yet"); return; } for (entry of entries) { if (!entry.isIntersecting) { video.pause(); continue; } await video.play(); } }; } document.querySelectorAll("video").forEach((video) => { video.pause(); new IntersectionObserver(videoObsever(video), { threshold: 0.2, }).observe(video); });
Hi Marcos, thanks for taking a look. Your second comment mostly works but does not play the video(s) that is in view when the page is first loaded. Here it is an example: https://benfrain.com/playground/video-stop-start/indexAlt.html
(In reply to Ben Frain from comment #5) > Hi Marcos, thanks for taking a look. Your second comment mostly works but > does not play the video(s) that is in view when the page is first loaded. > > Here it is an example: > https://benfrain.com/playground/video-stop-start/indexAlt.html Apologies, Ben. I was only checking if the AbortError was related to user activation and didn’t provide a complete solution. It should be possible to modify the code a bit to play the videos that are intersecting. Can you give that a go? Otherwise I’ll try to provide you with an updated solution tomorrow. If we can get it working I think we can close this issue 🤞
(In reply to Marcos Caceres from comment #6) > (In reply to Ben Frain from comment #5) > > Hi Marcos, thanks for taking a look. Your second comment mostly works but > > does not play the video(s) that is in view when the page is first loaded. > > > > Here it is an example: > > https://benfrain.com/playground/video-stop-start/indexAlt.html > > Apologies, Ben. I was only checking if the AbortError was related to user > activation and didn’t provide a complete solution. It should be possible to > modify the code a bit to play the videos that are intersecting. > > Can you give that a go? Otherwise I’ll try to provide you with an updated > solution tomorrow. If we can get it working I think we can close this issue > 🤞 Couldn't help myself... I think this works: ```JS function videoObsever(video) { return async (entries, observer) => { for (entry of entries) { if (!entry.isIntersecting) { video.pause(); console.log("paused", video); continue; } // Guard against playing too early if (video.readyState < video.HAVE_CURRENT_DATA) { console.warn("video not ready yet", video); await new Promise((r) => { video.addEventListener("canplay", r, { once: true }); }); console.log("video ready", video); } await video.play(); console.log("playing", video); } }; } document.querySelectorAll("video").forEach((video) => { video.pause(); new IntersectionObserver(videoObsever(video), { threshold: 0.2, }).observe(video); }); ``` In any case... Ben, is it ok if we close this issue? I think we've shown that Webkit is working as expected, right?
Thanks Marcos, I guess my question is, what about the original code was problematic? Is there a good reason this should not work in WebKit when it works in Firefox and Chromium browsers. If you are happy that WebKit is behaving here as you would hope and expect, by all means close. 👍
(In reply to Ben Frain from comment #8) > Thanks Marcos, > > I guess my question is, what about the original code was problematic? Is > there a good reason this should not work in WebKit when it works in Firefox > and Chromium browsers. Testing locally with Firefox I can see it sometimes also hits the situation where the video is not available. It may be thay the video was cached by those browsers so it was always ready to play? I think you would eventually need to add similar logic to what I added for when the video’s ready state doesn’t yet allow playing the video. You could test for that by maybe sending a bad video or delaying the response from the server. > If you are happy that WebKit is behaving here as you would hope and expect, > by all means close. 👍 Cheers and best of success with what you are building!