原生 video 循环需写成 ,loop 属性值无效;ios 必须同时满足 muted、autoplay、playsinline 才生效,否则需监听 ended 手动重播并处理 play() promise 拒绝。

直接写 <video loop></video> 就能循环,但不加 muted 和 autoplay,它大概率根本播不起来——更别说循环了。
为什么 loop="true" 或 loop="" 无效?
浏览器只检查 loop 属性是否存在,不读取它的值。写成 <video loop="true"></video>、<video loop=""></video> 甚至 <video loop="loop"></video>,都等同于没写。只有 <video loop></video> 或 <video loop src="x.mp4"></video> 才真正触发原生循环。
常见错误是把它当普通 HTML 属性处理,结果页面毫无反应,还怀疑是浏览器兼容性问题。
- JS 动态设置也一样:
video.loop = true有效,video.loop = "true"无效 -
video.setAttribute('loop', 'true')同样无效——它只是往 DOM 上加了个带值的属性,不改变 JS 层的loop状态 - 检查是否生效,用
video.hasAttribute('loop')或直接读video.loop(返回布尔值)
iOS Safari 要求 muted + autoplay 才认 loop
哪怕用户已经点过屏幕、视频也加载完成,iOS Safari(含微信内置浏览器)仍可能忽略 loop,除非同时满足 muted 和 autoplay。这是 Apple 的策略,不是 bug。
背景视频想稳定循环,必须写成:
<video loop muted autoplay playsinline src="bg.mp4"></video>
其中 playsinline 防止 iOS 强制全屏跳转;muted 绕过自动播放拦截。
- 如果视频必须有声,iOS 下基本无法可靠实现原生循环
- 只能退到监听
ended事件手动重播,但会引入可感知卡顿和play()Promise 拒绝风险 - Android 浏览器一般没这限制,但部分定制 ROM 可能模仿 iOS 行为
手动监听 ended 重播比原生 loop 更可控,但也更重
当原生 loop 失效,或你需要循环前做清理、加延迟、换源时,可以用 JS 监听 ended:
video.addEventListener('ended', () => {
video.currentTime = 0;
video.play().catch(e => console.warn('重播被阻止:', e));
});
关键细节:
-
video.play()返回 Promise,失败时会 reject,必须catch,否则报错打断逻辑 - 仅设
currentTime = 0不调play(),视频不会动 - 若用户未与页面交互过,iOS/Chrome 可能直接拒绝
play(),此时需引导点击授权 - 原生
loop是零成本的声明式行为;手动重播是显式、可干预、但也更易出错的命令式路径
最常被忽略的一点:在 iOS 上,哪怕你写了 loop、autoplay、muted、playsinline,只要视频文件本身编码不支持硬件解码(比如某些 HEVC 视频),或者容器格式异常(如 MP4 的 moov box 在末尾),循环仍会卡在最后一帧或静音失效——这时候得用 FFmpeg 重新封装,而不是改 HTML。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











