You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Alpine.js构建的模态框在Firefox生产环境需刷新页面才可自动播放视频的问题求助

Alpine.js构建的模态框在Firefox生产环境需刷新页面才可自动播放视频的问题求助

大家好,我遇到了一个挺棘手的兼容性问题,想请教下各位大佬:

我用Alpine.js在页面上做了一个模态框,点击“See how it works”按钮后,模态框弹出,里面的视频应该自动开始播放;点击模态框外部或者“Close video”按钮可以关闭模态框。

这个功能在Chrome、Safari不管是本地还是生产环境都完全正常,Firefox本地测试也没问题,但到了Firefox的生产环境,视频就是不会自动播放——模态框能正常打开关闭,但视频毫无反应,除非我刷新页面,之后再打开模态框视频才会正常自动播放。

有没有什么特定的处理方式,能让Firefox生产环境下不用刷新页面也能正常加载并自动播放视频呢?

下面是我这个模块的代码:

HTML代码

<div class="section-how-it-works" x-ignore ax-load x-data="{ modalOpen: false }">

<div x-ignore ax-load x-data="VideoModal">

<!-- how it works video button -->

<button id="open-modal-button" class="button button-secondary" aria-label="See how it works" @click="modalOpen = true" aria-controls="modal" >

<span class="text">See how it works</span>

<span class="icon">

<Icon name="video-play" size="16" />

</span>

</button>

<!-- how it works modal backdrop -->

<div class="modal-backdrop"

x-show="modalOpen"

x-transition:enter="transition ease-out duration-200"

x-transition:enter-start="opacity-0"

x-transition:enter-end="opacity-100"

x-transition:leave="transition ease-out duration-100"

x-transition:leave-start="opacity-100"

x-transition:leave-end="opacity-0"

aria-hidden="true"

x-cloak

></div>

<!-- how it works video modal -->

<div id="modal"

role="dialog"

aria-modal="true"

x-show="modalOpen"

x-transition:enter="transition ease-out duration-300"

x-transition:enter-start="opacity-0 scale-75"

x-transition:enter-end="opacity-100 scale-100"

x-transition:leave="transition ease-out duration-200"

x-transition:leave-start="opacity-100 scale-100"

x-transition:leave-end="opacity-0 scale-75"

x-cloak

x-cloak

>

<!-- video -->

<div class="video" x-data="{ play: true, mute: false, subtitles: false }" x-init="$refs.video.onplaying = () => { play = true }; $refs.video.onended = () => { play = false }; $watch('play', (value) => {

if (value) {

$refs.video.play();

} else {

$refs.video.pause();

}

})">

<figure class="video-inner" @click.outside="modalOpen = false" @keydown.escape.window="modalOpen = false">

<video id="how-it-works-video" x-ignore ax-load="lazy" playsinline x-init="$watch('modalOpen', value => value ? $el.play() : $el.pause())" x-ref="video" @click="play = !play">

<source src={howItWorksVideo} type="video/mp4"/>

<track label="English" kind="subtitles" src={subs} srclang="en" x-show="subtitles" x-bind:default="subtitles">

</video>

<!-- Video controls -->

<div id="video-controls" class="controls" data-state="hidden">

<div @click="play = !play">

<template x-if="!play">

<Icon name="vid-play" class="control"/>

</template>

<template x-if="play">

<Icon name="vid-pause" class="control"/>

</template>

</div>

<button class="progress" class="control">

<progress id="progress" value="0" min="0">

<span id="progress-bar"></span>

</progress>

</button>

<div @click="mute = !mute; $refs.video.muted = mute" class="mute">

<template x-if="!mute">

<Icon name="vol-on" class="control"/>

</template>

<template x-if="mute">

<Icon name="vol-off" class="control"/>

</template>

</div>

<div @click="subtitles = !subtitles; $refs.video.textTracks[0].mode = subtitles ? 'showing' : 'hidden'" class="subtitles">

<div x-show="!subtitles" class="tooltip">

<Icon name="subs-off" class="control"/>

<span class="tooltiptext">Enable captions</span>

</div>

<div x-show="subtitles" class="tooltip">

<Icon name="subs-on" class="control"/>

</div>

</div>

</div>

<!-- End of video controls -->

</figure>

</div>

<!-- close button -->

<button id="close-modal-button" class="button button-secondary" @click="modalOpen = false" aria-label="Close video">

<span class="text">Close video</span>

<span class="icon">

<Icon name="close" size="16" />

</span>

</button>

</div>

</div>

</div>

JS代码

export default () => {

return {

init() {

const heroVideo = document.getElementById('hero-video');

const openModalButton = document.getElementById('open-modal-button');

const closeModalButton = document.getElementById('close-modal-button');

const modal = document.getElementById('modal');

const header = document.getElementById('header');

const disableScroll = () => {

document.body.style.overflow = 'hidden';

document.addEventListener('touchmove', preventScroll, { passive: false });

};

const enableScroll = () => {

document.body.style.overflow = 'auto';

document.removeEventListener('touchmove', preventScroll, { passive: false });

};

const preventScroll = (event) => {

event.preventDefault();

};

//Open modal

openModalButton.addEventListener('click', () => {

heroVideo.pause();

modal.classList.add('open');

header.style.zIndex = '30';

disableScroll();

const video = document.getElementById("how-it-works-video");

const progress = document.getElementById("progress");

const progressBar = document.getElementById("progress-bar");

// Ensuring progress bar reflects video duration

video.addEventListener("loadedmetadata", () => {

progress.setAttribute("max", video.duration);

});

// Progress bar update to reflect current position in video

video.addEventListener("timeupdate", () => {

if (!progress.getAttribute("max")) {

progress.setAttribute("max", video.duration);

}

progress.value = video.currentTime;

progressBar.style.width = `${Math.floor((video.currentTime * 100) / video.duration)}%`;

});

// Allowing users to interact with progress bar / seek specific point in video

progress.addEventListener("click", (e) => {

const rect = progress.getBoundingClientRect();

const pos = (e.pageX - rect.left) / progress.offsetWidth;

video.currentTime = pos * video.duration;

});

});

//Close modal through close button

closeModalButton.addEventListener('click', () => {

heroVideo.play();

modal.classList.remove('open');

header.style.zIndex = '100';

enableScroll();

});

//Close modal with click outside of modal area

modal.addEventListener('click', (event) => {

if (event.target === modal) {

heroVideo.play();

modal.classList.remove('open');

header.style.zIndex = '100';

enableScroll();

}

});

//Close modal by pressing ESC

document.addEventListener('keydown', (event) => {

if (event.key === 'Escape' && modal.classList.contains('open')) {

heroVideo.play();

modal.classList.remove('open');

header.style.zIndex = '100';

enableScroll();

}

});

},

};

};

我自己初步猜是不是Firefox生产环境下的视频预加载或者Alpine的初始化时机问题?比如视频还没加载完成就触发了play(),但本地环境资源加载快所以没问题?或者是ax-load的lazy加载在Firefox生产下有延迟?

有没有大佬遇到过类似的情况,给点思路或者解决方案呀,万分感谢!

备注:内容来源于stack exchange,提问作者scubaM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 10:39:36