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




