如何修复iOS设备上图片触发视频的事件监听问题
iOS设备图片点击无法显示视频容器的解决方案
问题描述
我开发了一个网站,实现图片hover/点击时加载并播放视频的功能,该网站在除苹果iOS设备外的所有设备上运行正常。但在iOS设备上点击图片时,video容器无法显示。尝试过添加cursor: pointer或onClick事件监听器来修复,但未成功。
原代码
JavaScript
document.addEventListener("DOMContentLoaded", () => { const columns = document.querySelectorAll(".column"); columns.forEach((column) => { const thumbnail = column.querySelector(".thumbnail"); const videoContainer = column.querySelector(".video-container"); const video = videoContainer.querySelector("video"); let isVideoLoaded = false; video.addEventListener("loadeddata", () => { thumbnail.style.opacity = "0"; video.style.display = "block"; isVideoLoaded = true; }); document.addEventListener("click", handleClickOutside); if (window.matchMedia("(hover: hover)").matches) { // 支持hover的设备(桌面) column.addEventListener("mouseenter", handleMouseEnter); column.addEventListener("mouseleave", handleMouseLeave); column.addEventListener("click", handleClickInside); } else if (window.matchMedia("(hover: none)").matches) { // 不支持hover的设备(移动端) column.addEventListener("touchstart", () => { handleTouchStart(); handleClickInside(); }); column.addEventListener("touchend", handleTouchEnd); } async function handleTouchEnd(event) { if (!player.paused()) { return; } await loadVideoIfNotLoaded(); hideThumbnailAndPlay(); } async function loadVideoIfNotLoaded() { if (!isVideoLoaded) { await loadVideo(); } } async function loadVideo() { const dataSrc = video.getAttribute("data-src"); if (dataSrc && !isVideoLoaded) { return new Promise((resolve, reject) => { video.src = dataSrc; video.load(); video.addEventListener("loadeddata", () => { isVideoLoaded = true; resolve(); }); video.addEventListener("error", reject); }); } } function hideThumbnailAndPlay() { thumbnail.style.opacity = "0"; videoContainer.style.display = "block"; player.play(); } function handleMouseEnter() { if (!player.paused()) { return; } loadVideoIfNotLoaded() .then(hideThumbnailAndPlay) .catch((error) => { console.error("Error loading video:", error); }); } function handleMouseLeave() { thumbnail.style.opacity = "1"; player.pause(); player.currentTime(0); videoContainer.style.display = "none"; } function handleTouchStart(event) { loadVideoIfNotLoaded() .then(hideThumbnailAndPlay) .catch((error) => { console.error("Error loading video:", error); }); } function handleClickOutside(event) { const isClickInsideVideoContainer = event.target.closest(".video-container"); const videoContainers = document.querySelectorAll(".video-container"); if (!isClickInsideVideoContainer) { videoContainers.forEach((container) => { const video = container.querySelector("video"); const thumbnail = container.parentElement.querySelector(".thumbnail"); thumbnail.style.opacity = "1"; player.pause(); player.currentTime(0); container.style.display = "none"; }); } } function handleClickInside() { if (!video.paused) { handleClick(); } } function handleClick() { alert("Hi!"); } // 初始化视频播放器 const player = videojs(video, { autoplay: false, muted: true, loop: true, }); }); });
CSS
.container { display: grid; grid-template-columns: auto auto; gap: 5rem; } .column { position: relative; } .thumbnail { height: 100%; cursor: pointer; } .thumbnail img { width: 100%; height: 100%; pointer-events: none; } .video-container { position: absolute; top: 0; bottom: 0; width: 100%; height: 100%; overflow: hidden; } .video-container video { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: auto; height: auto; min-width: 100%; min-height: 100%; pointer-events: none; } @media (max-width: 650px) { .container { grid-template-columns: auto; } }
HTML
<head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link href="https://unpkg.com/video.js/dist/video-js.min.css" rel="stylesheet" /> <script src="https://unpkg.com/video.js/dist/video.min.js"></script> </head> <body> <div class="container"> <div class="column"> <div class="thumbnail"> <img src="https://images.unsplash.com/photo-1684482570533-b97679dd0705?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw4fHx8ZW58MHx8fHx8&auto=format&fit=crop&w=500&q=60" alt="" /> </div> <div class="video-container"> <video data-src="https://player.vimeo.com/external/431680358.sd.mp4?s=3f97b05e5d5dab21298a96bb5653849567aaf675&profile_id=164&oauth2_token_id=57447761" type="video/mp4"></video> </div> </div> <div class="column"> <div class="thumbnail"> <img src="https://images.unsplash.com/photo-1682685796766-0fddd3e480de?ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxlZGl0b3JpYWwtZmVlZHwzNnx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=60" alt="" /> </div> <div class="video-container"> <video data-src="https://player.vimeo.com/external/503759306.sd.mp4?s=273116dd8b495498766d3fe03e8416fff0cc68b5&profile_id=164&oauth2_token_id=57447761" type="video/mp4"></video> </div> </div> </div> </body>
解决方案
针对iOS设备的问题,需要从视频播放限制、元素初始状态、事件逻辑三个方面修复:
1. 修复视频容器初始显示状态
iOS下元素默认显示逻辑更严格,给.video-container添加默认隐藏样式:
.video-container { position: absolute; top: 0; bottom: 0; width: 100%; height: 100%; overflow: hidden; display: none; /* 添加这行 */ }
2. 适配iOS视频播放规则
iOS要求视频必须添加playsinline和webkit-playsinline属性才能在容器内播放(而非全屏),同时video标签需要添加video.js的默认类:
<video class="video-js" <!-- 添加这个类 --> playsinline webkit-playsinline <!-- 添加这两个属性 --> data-src="你的视频地址" type="video/mp4" ></video>
3. 修正事件逻辑与变量引用问题
- 移除重复的touchend事件处理:touchstart已经完成了视频加载和播放触发,touchend的逻辑会导致重复操作,直接删除移动端的touchend监听:
// 注释或删除这行 // column.addEventListener("touchend", handleTouchEnd);
同时删除handleTouchEnd函数,避免冗余代码。
- 修复handleClickOutside中的player引用错误:原代码循环内的player变量会被覆盖,导致点击外部时只操作最后一个视频,需要通过video元素获取对应的player实例:
function handleClickOutside(event) { const isClickInsideVideoContainer = event.target.closest(".video-container"); const videoContainers = document.querySelectorAll(".video-container"); if (!isClickInsideVideoContainer) { videoContainers.forEach((container) => { const video = container.querySelector("video"); const thumbnail = container.parentElement.querySelector(".thumbnail"); // 通过video元素获取对应的player实例 const currentPlayer = videojs.getPlayer(video); thumbnail.style.opacity = "1"; currentPlayer.pause(); currentPlayer.currentTime(0); container.style.display = "none"; }); } }
- 统一视频加载后的显示逻辑:确保视频已加载再执行播放操作,避免iOS下的交互限制:
function hideThumbnailAndPlay() { thumbnail.style.opacity = "0"; videoContainer.style.display = "block"; // 确保视频已加载再播放 if (isVideoLoaded) { player.play(); } }
4. 确保video.js初始化正确
在初始化player时,添加playsinline: true配置,适配iOS:
const player = videojs(video, { autoplay: false, muted: true, loop: true, playsinline: true // 添加这个配置 });
内容的提问来源于stack exchange,提问作者Rocky Barua
相关产品推荐
相关产品推荐

