Firefox 不会调用 HTML5 视频的 canplay/canplaythrough 事件。为啥?

Posted

技术标签:

【中文标题】Firefox 不会调用 HTML5 视频的 canplay/canplaythrough 事件。为啥?【英文标题】:The canplay/canplaythrough events for an HTML5 video are not called on Firefox. Why?Firefox 不会调用 HTML5 视频的 canplay/canplaythrough 事件。为什么? 【发布时间】:2012-05-01 10:19:55 【问题描述】:

我正在构建一个用于管理 html5 视频的 jQuery 插件。我正在尝试捕获 canplay 和 canplaythrough 事件。在 Chrome 中,事件被触发没有问题。在 Firefox 中,有时会触发,有时不会。

我在这里稍微简化一下我的代码:

$('#my_video').on('canplay canplaythrough', function()
    console.log('canplay event fired');
);

我也尝试使用原生 javascript .addEventListener(),但它不起作用。

知道为什么在 Firefox 上没有调用该事件以及如何解决这个问题吗?

注意:请不要告诉我使用已经可用的插件之一,例如 jplayer 和 video-js,我知道它们存在并且运行良好,但我必须构建一个内部解决方案。

【问题讨论】:

【参考方案1】:

问题是您的video 元素在您注册事件处理程序之前触发了canplaythrough 事件。

正如您在自己的回答中指出的那样,您可以将脚本放在<head> 中,但这对您的页面性能不利。

解决问题的更好方法是检查readystate 属性并在这种情况下手动执行您的函数:

var $video = $('video'),
    videoElement = $video[0];

$video.on('canplaythrough', callback);

// If the video is in the cache of the browser,
// the 'canplaythrough' event might have been triggered
// before we registered the event handler.
if (videoElement.readyState > 3) 
  callback();

【讨论】:

最佳答案!检查视频就绪状态,如果 readyState 不是4 或更高,则添加事件监听器。 惊人的答案!就是这个。谢谢!!!! @Gabriel 你应该把它标记为肯定的答案 将脚本放在头部是不好的:整体编写脚本并显示缺乏不理解的标准:使用defer 属性,将脚本放在头部并且仍然具有相同的性能。 @John:我很确定将<script> 标记添加到带有<head>defer 会导致与@Gabriel 描述的相同的问题。 不,您总是将脚本保留在 head 元素中,然后然后解决问题。将脚本移动到 body 元素是不好的做法,甚至考虑在 bodyhead 元素之间移动脚本也是对 JavaScript 的业余水平理解。【参考方案2】:

您看到此问题的最可能原因可能与时间问题有关。您在接受的答案中表示,将 jQuery 放在头部而不是页脚可以解决问题。这告诉我问题在于 DOM 解析和脚本执行顺序。最可能的罪魁祸首是“canplay”和“canplaythrough”事件在 jquery 和您的页面脚本被解析并添加事件处理程序之前被触发 - 但只是有时,取决于网络流量和加载时间。通过将脚本放在头部,您强制在创建 DOM 元素之前发生事件绑定,从而确保您不会错过任何事件。

顺便说一句,将脚本元素放在页面底部的性能优势值得商榷。如果您真的想调整页面性能,请使用 LABjs 之类的工具来管理并行脚本加载。

【讨论】:

【参考方案3】:

在我的例子中,这是由为元素指定的preload 属性确定的。我根本没有指定它,所以不同的浏览器选择做不同的事情。

一旦我指定了preload="auto"on("canplay") 事件处理程序就可以正常工作/符合预期。

【讨论】:

【参考方案4】:

即使我的问题没有引起任何关注,我认为为将来可能偶然发现此问题的人提供一个解释是个好主意...

问题真的很奇怪:如果 jQuery 核心包含在页脚中,则某些视频事件不起作用。如果 jQuery 核心包含在文档的头部,则所有事件都被正确调用。

因此解决方案是在 html 头部包含 jQuery 核心,即使优化的“最佳实践”建议将所有脚本放在正文末尾以加快加载时间。

【讨论】:

我敢打赌它实际上是一个竞争条件,canplaycanplaythrough 事件被触发 before jQuery 附加事件侦听器。编辑:刚刚注意到 Clayton Gulick 讨论了下面的问题。 或者您可以使用 javascript 来连接事件侦听器,然后以编程方式设置视频的 src 属性。您可以将 src 存储在 data-src 属性中,然后运行 ​​videoElement.src = videoElement.dataset['src']【参考方案5】:

我也确实认为这是一种竞争条件。我绕过它的方法如下:

在视频元素的 HTML 中添加属性 preload="metadata" - 仅预加载视频元数据。所以:

<video id="myVideo"   preload="metadata" />

接下来,在 JS 内部:

var $vid = $("#myVideo");
$vid.bind("canplaythrough", console.log("can play through full video"));
$vid.get(0).load();

这在 Chrome 中为我记录了消息 - 尚未在其他地方测试。

【讨论】:

【参考方案6】:

如果您希望 Firefox 在您触发 load 后加载整个音频,那么它不会这样做。它将触发loadstart,但不会下载任何内容。不会触发 progress 事件。它实际上不会对该文件提出任何请求。您可以在 Firebug 中看到这种行为。

Firefox 只会在您触发“播放”后开始加载文件。

Proof。查看控制台输出。

【讨论】:

其实这种行为是意料之中的,可以通过视频的preload属性来控制 @NamanGoel The preload attribute specifies if and how the author thinks that the audio file should be loaded when the **page loads**. 据我所知,preloadaudio.load() 无关。相反,当没有对音频执行任何操作时,它会更改浏览器行为。如果我弄错了,请指出我的文档。是否可以在页面加载时强制音频不加载,而是在触发load() 时完全加载? 我现在无法处理链接。但据我所知,浏览器不应该加载音频,除非它有preload attr。无论如何,有些浏览器会预先加载音频/视频,看起来很快。【参考方案7】:

添加:

var video = $('video');
video.trigger('load');

之后添加 canplaythrough 事件侦听器为我在 Firefox 和 Safari 上修复了它。

【讨论】:

【参考方案8】:

从这里尝试不同的方法 1.5 天后(原版)。带有 addEventListner 的事件“canplay”和“canplaythrough”在 Edge 中不起作用。它们在其他所有浏览器中都能正常工作。所以我最后用 setTimout 检查了“就绪状态”:-(。不是很优雅,但它对我有用。

Controller.prototype.backGroundControl = function () 
var vid = document.querySelector('#bgvid');
var bgImg = document.querySelector('#bgimg');
_this = this;

if (vid.readyState === 4)
    bgImg.style.opacity = "0"
 else
    setTimeout(function()
        _this.backGroundControl();
    ,500)
  

【讨论】:

以上是关于Firefox 不会调用 HTML5 视频的 canplay/canplaythrough 事件。为啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 Firefox 中单击 HTML5 视频控件时如何防止事件冒泡

HTML5 播放率和 Firefox

HTML5 视频不会在 Android 设备上循环播放

暂停视频不会停止 html5 视频标签中的音频

Firefox 特定的 html5 视频错误

Firefox 的 HTML5 背景视频仍然无法播放