Hi all,
I’m facing a strange issue with a custom JavaScript setup I’ve implemented inside a Bubble HTML element. I’m using this approach instead of a plugin because I need complete control over video playback, including features like:
- Manual play/pause control
- Capturing timestamps for commenting at specific points in the video
How It Works
- I have a list of videos uploaded by users.
- When a user clicks on a video, I open a modal, and inside it, I render and play the video using my JavaScript (via HTML element).
- The JavaScript initializes the video player, fetches the duration, and handles events like play, pause, etc.
The Problem
- The first video works perfectly after page load — it plays, I get the total duration, and all controls behave as expected.
- But when I try to open a second video (in the same modal or a different one), it fails to initialize properly.
- No duration.
- It doesn’t start playing until I close and reopen the modal again.
- I initially suspected it might be a DOM cleanup issue — e.g., needing to remove event listeners or reset the video tag before loading the next one. However, even after implementing such cleanup logic, the issue persists.
What I’ve Tried
- Removing and re-adding event listeners on modal close
- Checking if the video element is properly appended to the DOM
Important Note on Iframe Option
One workaround I explored was enabling the “Display as an iframe” option in Bubble’s HTML element.
When I do this, the video renders and plays correctly every time.
However, this approach prevents my Workflow JavaScript from accessing the video element. This breaks my custom commenting feature, which depends on full DOM access.
So, using an iframe is only a viable solution if you don’t need to interact with the video via JS.
What I’m Looking For
If anyone has dealt with a similar JavaScript-in-Bubble + modal video setup, I’d love some guidance:
- Is there a reliable way to completely reset the HTML element or JavaScript context between modal opens?
- Could this be related to how Bubble handles DOM lifecycle in modals?
- Any other recommended strategies for working with multiple videos using raw JavaScript inside Bubble?
My Javacript:
<script>
const video = document.getElementById('video');
const timestampBar = document.getElementById('timestampBar');
const timeDisplay = document.getElementById('timeDisplay');
const fullscreenButton = document.getElementById('fullscreenButton');
const playPauseButton = document.getElementById('playPauseButton');
const rewindButton = document.getElementById('rewindButton');
const forwardButton = document.getElementById('forwardButton');
let isDragging = false;
function formatTime(seconds) {
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}:${secs < 10 ? '0' : ''}${secs}`;
}
function updateTimestampBar() {
const playedPercentage = (video.currentTime / video.duration) * 100;
timestampBar.style.background = `linear-gradient(to right, #000000 ${playedPercentage}%, #f0ebe7 ${playedPercentage}%)`;
}
function updateVideoTime(event) {
const rect = timestampBar.getBoundingClientRect();
const x = event.clientX - rect.left;
const ratio = Math.min(Math.max(x / rect.width, 0), 1);
video.currentTime = ratio * video.duration;
}
// Video Controls
playPauseButton.addEventListener('click', () => {
if (video.paused) {
video.play();
playPauseButton.src = "https://57ae69b95571dc15db44eb6c6b12a974.cdn.bubble.io/f1723118792460x382336143357890400/61180.png";
} else {
video.pause();
playPauseButton.src = "https://57ae69b95571dc15db44eb6c6b12a974.cdn.bubble.io/f1723118045829x874719752319035500/play-button.svg";
}
});
rewindButton.addEventListener('click', () => {
video.currentTime = Math.max(video.currentTime - 10, 0);
});
forwardButton.addEventListener('click', () => {
video.currentTime = Math.min(video.currentTime + 10, video.duration);
});
fullscreenButton.addEventListener('click', () => {
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.webkitRequestFullscreen) {
video.webkitRequestFullscreen();
} else if (video.mozRequestFullScreen) {
video.mozRequestFullScreen();
} else if (video.msRequestFullscreen) {
video.msRequestFullscreen();
}
});
// Timestamp scrubbing
timestampBar.addEventListener('mousedown', (e) => {
isDragging = true;
updateVideoTime(e);
});
document.addEventListener('mousemove', (e) => {
if (isDragging) updateVideoTime(e);
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
// Video Time Updates
video.addEventListener('timeupdate', () => {
updateTimestampBar();
timeDisplay.textContent = `${formatTime(video.currentTime)} / ${formatTime(video.duration)}`;
});
video.addEventListener('loadedmetadata', () => {
updateTimestampBar();
timeDisplay.textContent = `${formatTime(0)} / ${formatTime(video.duration)}`;
});
// Click video to toggle play/pause
video.addEventListener('click', () => {
playPauseButton.click();
});
</script>
Thanks in advance!