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

视频观看至90%时‘Next video’按钮偶现不显示的修复方案

修复视频观看至90%时“Next video”按钮偶现不显示的问题

问题背景

我尝试使用JavaScript追踪视频的观看时长,需求是当用户观看视频至90%时显示“Next video”按钮,但该按钮有时正常显示,有时却不显示。以下是完整的HTML及JavaScript代码:

<head>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<div class="cursor-pointer pt-4 d-flex justify-content-center align-items-center flex-column">
   <video class="w-50" poster="" id="plyr-video-player" playsinline controls>
      <source src="https://therapywebsite.s3.amazonaws.com/Binaural+Beats+Fast+5+min+meditation+Stress+Relief+%5Bxc3MacYfdbI%5D.mp4" type="video/mp4" />
   </video>
</div>
<div class="pt-4 d-flex justify-content-center align-items-center flex-column">
   <h5>Welcome to this session</h5>
   <button class=" d-none w-25 btn btn-primary me-3" id="next-video-btn">Next video</button>
</div>

<script>
    document.addEventListener("DOMContentLoaded", (event) => {
      const video = document.getElementById("plyr-video-player");
      const nextVideoBtn = document.getElementById("next-video-btn");

      const videoId = 1;
      const postInterval = 10;

      let playedCount = 0;
      let lastPlayedTime = 0;
      let totalWatchedTime = 0;
      let actualWatchedTime = 0;
      let isTabActive = true;
      let skipDetected = false;
      let isVideoVisible = true;

      document.addEventListener("visibilitychange", () => {
        isTabActive = !document.hidden;
      });

      const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          isVideoVisible = entry.isIntersecting;
        });
      }, {threshold: 0.5});
      observer.observe(video);

      video.addEventListener("loadedmetadata", () => {
        const videoDuration = Math.floor(video.duration);
        const playedCountThreshold = Math.floor(videoDuration * 0.9);

        if (videoDuration){
          console.log("Metadata loaded")
        } else{
          console.log("Metadata isn't loaded yet!!")
        }

        const showVideoBtn = () =>{
          if (video.currentTime > playedCountThreshold) {
            if (nextVideoBtn.classList.contains("d-none")) {
              nextVideoBtn.classList.remove("d-none");
            }
          }
        };

        function autoPlay() {
          return video.play();
        }
        autoPlay().catch(error => {
          console.error("Autoplay failed:", error);
        });
        setTimeout(autoPlay, 1000)

        function trackVideo(eventType, currentTime){
          currentTime = Math.floor(currentTime);
          if (isTabActive && isVideoVisible){
            if (currentTime > playedCountThreshold && lastPlayedTime <= playedCountThreshold) {
              playedCount++;
            }
            if (eventType !== "play" || eventType === "timeupdate"){
              const timeDiff = Math.max(0, currentTime - lastPlayedTime);
              actualWatchedTime += timeDiff;
            } else if (eventType === "seeked") {
              skipDetected = true;
              const skippedTime = Math.max(0, currentTime - lastPlayedTime);
              actualWatchedTime -= skippedTime;
            }
            totalWatchedTime = actualWatchedTime;

            fetch("/track_video/", {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
                "X-CSRFToken": "{{ csrf_token }}"
              },
              body: JSON.stringify({
                video_id: videoId,
                played_count: playedCount,
                video_duration: videoDuration,
                last_played_time: lastPlayedTime,
                total_watched_time: totalWatchedTime,
              })
            });
          }
          lastPlayedTime = currentTime;
        }

        video.addEventListener("play", () => trackVideo('play', video.currentTime));
        video.addEventListener("pause", () => trackVideo("pause", video.currentTime));
        video.addEventListener('timeupdate', () => {
          if (video.currentTime - lastPlayedTime >= postInterval) {
            trackVideo('timeupdate', video.currentTime);
          }
          showVideoBtn();
        });
        video.addEventListener("seeked", () =>{
          skipDetected = true;
          lastPlayedTime = Math.floor(video.currentTime)
          trackVideo("seeked", video.currentTime)
        });
      });
      nextVideoBtn.addEventListener("click", () => {
        window.location.href = "/next_video.html";
      });
    });
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>

修复方案

1. 补充showVideoBtn的触发场景

当前仅在timeupdate事件中调用showVideoBtn,但用户直接快进至90%后、暂停在90%后,timeupdate不会触发,导致按钮不显示。需要在pause、seeked、ended事件中也触发按钮检查:

// 修改pause事件监听
video.addEventListener("pause", () => {
  trackVideo("pause", video.currentTime);
  showVideoBtn(); // 新增
});

// 修改seeked事件监听
video.addEventListener("seeked", () =>{
  skipDetected = true;
  lastPlayedTime = Math.floor(video.currentTime);
  trackVideo("seeked", video.currentTime);
  showVideoBtn(); // 新增
});

// 新增ended事件监听
video.addEventListener("ended", showVideoBtn);

2. 优化showVideoBtn的判断逻辑

  • 增加视频时长有效性校验,避免元数据加载异常时的误判
  • 将判断条件从>改为>=,覆盖刚好到达90%时长的边界场景
  • 直接移除d-none类,无需提前判断,简化逻辑且更可靠:
const showVideoBtn = () =>{
  if (!videoDuration) return; // 校验时长有效性
  if (video.currentTime >= playedCountThreshold) {
    nextVideoBtn.classList.remove("d-none");
  }
};

3. 确保阈值的实时可用性

将playedCountThreshold的计算移到showVideoBtn内部,避免因loadedmetadata未触发导致变量未定义的问题:

const showVideoBtn = () =>{
  if (!video.duration) return;
  const playedCountThreshold = Math.floor(video.duration * 0.9);
  if (video.currentTime >= playedCountThreshold) {
    nextVideoBtn.classList.remove("d-none");
  }
};

4. 增加页面可见性变化时的检查

当页面从不可见切换回可见时,检查当前播放进度是否已达阈值,避免用户切回页面时按钮未显示:

document.addEventListener("visibilitychange", () => {
  isTabActive = !document.hidden;
  if (isTabActive) {
    showVideoBtn(); // 新增
  }
});

内容的提问来源于stack exchange,提问作者tribhuwan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 17:21:02