You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

HTML5 Audio对象loadedmetadata事件未触发问题求助

解决Audio元素loadedmetadata事件未触发的问题

嘿,我完全懂你遇到的这个头疼问题——这事儿我之前做音频播放器的时候也踩过一模一样的坑!

问题根源

你猜得没错,就是事件绑定时机太晚了。当$(document).ready()执行的时候,浏览器可能已经完成了音频元数据的加载,loadedmetadata事件早就触发过了,这时候你再绑定事件处理函数,自然就捕获不到它了。

$(document).ready()只保证DOM结构加载完成,但音频资源的加载是异步的,完全可能在DOM就绪之前就已经加载完元数据了,尤其是当音频被浏览器缓存过的时候,这种情况会更常见。

另外还有个小细节:你写的$('document').ready()其实是个小错误——应该是$(document).ready()(去掉document外面的引号),不过这个不是导致事件不触发的核心原因,但规范写法还是要修正~

解决方案

我们需要做的是:先检查音频是否已经加载完元数据,如果已经加载就直接执行逻辑;如果还没加载,再绑定事件等待触发。这样就能覆盖所有情况了。

修正后的完整代码

<div class="caster-player">
  <div class="caster-player__controls">
    <progress class="caster-player__progress" value="0"></progress>
    <span class="caster-player__currenttime caster-player__time">00:00:00</span>
    <span class="caster-player__duration caster-player__time">00:00:00</span>
  </div>
  <audio src="https://bythecoverpodcast.com/wp-content/uploads/2017/12/01-No-Money.mp3"></audio>
  <p>"No Money" from No Money by Galantis.</p>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
  <script>
    $(document).ready(function() {
      var audioObj = $('audio');
      var audioEl = audioObj[0];
      var progress = $('.caster-player__progress');
      var duration = $('.caster-player__duration');

      function toHHMMSS(totalsecs) {
        var sec_num = parseInt(totalsecs, 10);
        var hours = Math.floor(sec_num / 3600);
        var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
        var seconds = sec_num - (hours * 3600) - (minutes * 60);
        if (hours < 10) { hours = "0" + hours; }
        if (minutes < 10) { minutes = "0" + minutes; }
        if (seconds < 10) { seconds = "0" + seconds; }
        return hours + ':' + minutes + ':' + seconds;
      }

      function setupProgress() {
        // 检查音频时长是否有效(元数据加载完成的标志)
        if (!isNaN(audioEl.duration)) {
          progress.attr('max', Math.floor(audioEl.duration));
          duration.text(toHHMMSS(audioEl.duration));
        } else {
          // 用one()确保事件只执行一次,避免重复绑定
          audioObj.one('loadedmetadata', setupProgress);
        }
      }

      // 立即执行初始化检查
      setupProgress();
    });
  </script>
</div>

关键改进点

  • 新增了setupProgress函数,先判断audioEl.duration是否为有效数字:如果不是NaN,说明元数据已经加载完成,直接设置progress的max值和显示时长;
  • 如果元数据还没加载,就用one()绑定loadedmetadata事件(one()on()更合适,因为这个事件只会触发一次,避免重复执行逻辑);
  • 修正了$(document).ready()的写法,符合jQuery规范。

另一种备选方案:直接在HTML中绑定事件

如果你想更早绑定事件,可以直接在audio标签上用onloadedmetadata属性,这样事件绑定会在DOM解析时就完成,绝对不会错过事件:

<audio src="https://bythecoverpodcast.com/wp-content/uploads/2017/12/01-No-Money.mp3" onloadedmetadata="handleLoadedMetadata()"></audio>

然后把处理函数放在全局作用域(或者确保能访问到相关DOM元素):

function handleLoadedMetadata() {
  var progress = $('.caster-player__progress');
  var duration = $('.caster-player__duration');
  // this指向当前audio元素
  progress.attr('max', Math.floor(this.duration));
  duration.text(toHHMMSS(this.duration));
}

这个方案的好处是绑定时机更早,适合对时效性要求高的场景。

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

火山引擎 最新活动