Open croxton opened 2 years ago
Hmmm, this is way above my pay grade. Are you willing to look into the issue?
It sounds like a Safari bug to me, but if we can work around it I'm willing to look at the code...
Good to know, cheers. Yes it does feel like a Safari bug (it really is the new IE, sigh).
I'm going to try some experiments to see if I can clone or extend the browser's default web components, so I can re-initialise them when swapped. I'll update this issue if I get anywhere. Thanks again!
For anyone running into this in the future, the only reliable solution I found was to insert / remove <video>
from a placeholder in the dom using javascript, rather than have htmx swap and cache the raw markup.
This is how I'm using it, with lazy loading as a bonus. The idea here is that the update()
method of this component is called when htmx swaps content:
/**
* Background video
*
* Mount and play a background video (autoplay, muted)
*/
/**
* Example markup
*
<div class="w-full h-full"
data-background-video
data-src-mp4="/my/video.mp4"
data-src-webm="/my/video.webm"
data-class="w-full h-full object-cover">
</div>
*/
import BaseComponent from '../modules/baseComponent';
export default class BackgroundVideo extends BaseComponent {
videoObserver = null;
selector = '[data-background-video]';
constructor() {
super();
this.mount();
}
mount() {
// lazy load videos as they enter the viewport
const videos = document.querySelectorAll(this.selector);
let self = this;
if ("IntersectionObserver" in window) {
this.videoObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(videoEntry) {
if (videoEntry.isIntersecting) {
let videoContainer = videoEntry.target;
let video = document.createElement("video");
if (video.canPlayType("video/webm")) {
let src = videoContainer.dataset.srcWebm;
video.setAttribute("src", src);
} else {
let src = videoContainer.dataset.srcMp4;
video.setAttribute("src", src);
}
// autoplay video - note that it must be muted
video.autoplay = true;
video.loop = true;
video.playsinline = true;
video.muted = true;
// add classes
let cssClass = videoContainer.dataset.class;
if (cssClass) {
video.setAttribute("class", cssClass);
}
// insert into dom
videoContainer.appendChild(video);
// when the video is able to play, add a class to the container for unveil animation
video.oncanplay = function() {
videoContainer.classList.add('can-play');
}
// kill observer
self.videoObserver.unobserve(videoContainer);
}
});
});
videos.forEach(function(video) {
self.videoObserver.observe(video);
});
}
}
destroy() {
let videos = document.querySelectorAll(this.selector);
for (let [i, videoContainer] of [...videos].entries()) {
videoContainer.innerHTML = null;
this.videoObserver.unobserve(videoContainer);
}
this.videoObserver = null;
videos = null;
}
unmount() {
if (this.mounted) {
// cleanup
this.destroy();
// remove component reference
this.ref = null;
}
}
update(e) {
// Update strategy:
// - remove all videos from the dom
// - unobserve video containers
// - re-initialise
if (document.contains(document.querySelector(this.selector))) {
// cleanup
this.destroy();
// mount again
this.mount();
}
}
}
Spent tons of hours, testing different lazyload libs getting same weird results in my posts infinite timeline: iOS (latest) Safari with devTools showed the <img>
was dynamically set with valid src
attr, but still not visible... And then I decided to dive into htmx issue tracker, doesn't expect to find some useful notes about that and....
I see, that there is no core workarounds yet, but now I now what to look at, thanks. If any updates, will be happy a lot, also if I find a working solution for my case, will paste it here.
Hi all. Looks like Unpoly have found a fix for this. Could the same approach be applied for HTMX?
Issue: https://github.com/unpoly/unpoly/issues/432#event-10978912949 Fix: https://github.com/unpoly/unpoly/commit/8044fd2a5a6c87c2936fd113dfc423d558a13630
I discovered this issue when trying to swap a native
<video>
element into a<div>
.<video>
has a default Shadow DOM for the player chrome and controls.video.html
:Chrome and Firefox render a ShadowRoot for the video element correctly, and display the native video controls. In Safari the Shadow DOM is empty and no controls are displayed - but the movie can still be controlled programatically (the movie plays but is blank when paused).
I'm not really sure what the expected behaviour is when swapping a web component, and if it differs for 'native' and custom web components. Are Chrome and Firefox cloning the Shadow DOM tree when htmx swaps an element, and Safari isn't? Or are they recreating it automatically after the swap because
<video>
has a default shadow dom? Is there a way to re-initialise the default shadow DOMs of elements like<video>
?