Safari 下同页面创建的视频超过 16 个则无法播放

最近在做一些视频相关的东西。在 Safari 下发现这样一个问题:<video> 标签创建多了,会导致同一个页面内后续的视频都无法播放。不管是 iOS 的 Safari,还是 macOS 下的,都存在同样的问题。尤其是 iOS 下,超过 16 个的视频几乎必定无法播放;效果就是显示一个播放按钮并且有一条斜线。

无法播放的视频截图

通过 video.error来检查最后的错误,得到的是 MEDIA_ERR_DECODE;同时,video.play() 方法会 reject 一个 AbortError。在 Google 检索了很久也没有得到有用的信息。

为了演示这个 bug,一个简单的示例的源码被我放在了 gist 上。你可以通过在 Safari 上打开 RawGit 提供的链接 来测试这个 bug。你只需要点击“测试”,并在视频开始播放后再次点击“测试”,如此循环 16 次,便能得到和我上一张图片一样的结果。

示例的代码如此简单,以至于我认为这个 bug 是几乎不可能发生的;它仅仅是调整了一个 DOM 的 innerHTML 来创建一个 video 元素(事实上无论通过何种方法创建,都会有这个问题;playsinline 属性似乎是必要的),然后再删除它,循环 16 次而已。

hello.innerHTML = '';
var src = 'https://cdn.rawgit.com/mediaelement/mediaelement-files/4d21a042/big_buck_bunny.mp4';
hello.innerHTML = '<video id="video" webkit-playsinline playsinline controls src="' + src + '">';
setTimeout(function () {
    document.getElementById('video').play();
}, 500);

目前,我没有发现任何能够解决这个 bug 的方法。可能唯一的办法就是,播放 15 个视频之后,刷新一下整个页面吧。

使用 InstantClick 时 piwik 、 MathJax 的配置

InstantClick 是一个利用 hover 和 click 的时间差,预加载网页的黑科技。原理主要是在 hover 的时候预读网页,然后点击的时候就替换整个页面,让你的网站看起来整个都 PJAX 过~很酷炫。

不过造成的问题也很显然:换页的时候很多统计工具会失效;像我博客用的 piwik 就无法统计到页面的切换。同样,MathJax 这类渲染页面的插件也失效了。

解决方法很简单,只需在初始化之前,监听 InstantClick 对象的 change 事件,在事件中处理统计和渲染操作。代码如下:

InstantClick.on('change', function() {
    //piwik
    _paq.push(['setDocumentTitle', document.title]);
    _paq.push(['trackPageView']);
    //mathjax
    MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
});
InstantClick.init();

Enjoy ~

innerTEXT,outerHTML,outerTEXT和firefox

Firefox 不支持 DOM 对象的 outerHTML innerText outerText 属性……这是个一直困扰大家(包括我)很久的问题,网上也有不少的解决方法,这里摘录如下,收藏备用。(源链接:http://www.w3help.org/zh-cn/causes/SD9017)

解决方案

在 Firefox 中,可通过扩展 HTMLElement 的原型 (prototype) 来实现相关属性。

  1. 扩展 Firefox 中 DOM 元素的 outerHTML 属性:

    if (typeof(HTMLElement) != "undefined") {
       HTMLElement.prototype.__defineSetter__("outerHTML", function(s) {
            var r = this.ownerDocument.createRange();
            r.setStartBefore(this);
            var df = r.createContextualFragment(s);
            this.parentNode.replaceChild(df, this);
            return s;
        });
       HTMLElement.prototype.__defineGetter__("outerHTML", function(){
            var a = this.attributes, str = "<" + this.tagName, i = 0;
            for (; i < a.length; i++)
                if (a[i].specified)
                    str += " " + a[i].name + '="' + a[i].value + '"';
            if (!this.canHaveChildren)
                return str + " />";
            return str + ">" + this.innerHTML + "</" + this.tagName + ">";
        });
    
        HTMLElement.prototype.__defineGetter__("canHaveChildren", function(){
            return !/^(area|base|basefont|col|frame|hr|img|br|input|isindex|link|meta|param)$/.test(this.tagName.toLowerCase());
        });
    }
  2. 扩展 Firefox 中 DOM 元素的 innerText 属性:

    if (!!document.getBoxObjectFor || window.mozInnerScreenX != null) {
        HTMLElement.prototype.__defineSetter__("innerText", function(sText) {
            var parsedText = document.createTextNode(sText);
            this.innerHTML = "";
            this.appendChild(parsedText);
            return parsedText;
        });
        HTMLElement.prototype.__defineGetter__("innerText", function() {
            var r = this.ownerDocument.createRange();
            r.selectNodeContents(this);
            return r.toString();
        });
    }
  3. 扩展 Firefox 中 DOM 元素的 outerText 属性:

    if (!!document.getBoxObjectFor || window.mozInnerScreenX != null) {
        HTMLElement.prototype.__defineSetter__("outerText", function(sText) {
            var parsedText = document.createTextNode(sText);
            this.parentNode.replaceChild(parsedText, this);
            return parsedText;
        });
        HTMLElement.prototype.__defineGetter__("outerText", function() {
            var r = this.ownerDocument.createRange();
            r.selectNodeContents(this);
            return r.toString();
        });
    }