style 元素的 sheet 属性指向的样式表对象在老版 WebKit 内核上有崩溃 Bug

南漂一卒 发布于 2015/06/09 19:15
阅读 331
收藏 1

因为需要美化 HTML 原生的 input[type="range"] 控件,但除了 IE 10+,Chrome、Firefox 都没提供对滑杆两端分别设置样式的伪元素,只好用 :before 来做“已选范围”的填充效果,而且需要 JavaScript 在滑块移动时伸缩填充色块……

那么问题来了,正因为是伪元素,不能用 DOM 的样式操作方法,只能去修改相应的样式表属性,这就必须遍历 document.styleSheets,找到匹配的 CSS 规则后用闭包缓存,在用户操作时批量修改 width 属性(之所以一刀切,因为 CSS 规则优先级算法又是一个庞杂的逻辑,而且每次修改前都要用它计算改哪个有效),代码如下 ——

(function (BOM, DOM, $) {

    //  把一个样式表集合中包含 伪类/伪元素的 CSS 规则 提取出来
    $.cssPseudo = function () {
        var Pseudo_Rule = [ ],
            Pseudo_RE = /:{1,2}[\w\-]+/g;

        $.each(arguments[0] || DOM.styleSheets,  function () {
            var iRule = this.cssRules;
            if (! iRule)  return;

            for (var i = 0, iPseudo;  i < iRule.length;  i++)
                if (! iRule[i].cssRules) {
                    iPseudo = iRule[i].cssText.match(Pseudo_RE);
                    if (! iPseudo)  continue;


                    for (var j = 0;  j < iPseudo.length;  j++)
                        iPseudo[j] = iPseudo[j].split(':').slice(-1)[0];
                    iRule[i].pseudo = iPseudo;
                    iRule[i].selectorText = iRule[i].selectorText ||
                        iRule[i].cssText.match(/^(.+?)\s*\{/)[1];
                    Pseudo_Rule.push(iRule[i]);
                } else
                    arguments.callee.call(iRule[i], i, iRule[i]);
        });

        return Pseudo_Rule;
    };

    //  $.fn.cssRule() 创建一个包含与 DOM 元素关联的样式表的 style 元素(并插入 body)后,
    //  把相应的 样式表对象 作为第一参数返回
    function Pseudo_Bind() {
        var iStyleSheet = $.cssPseudo([arguments[0]]),
            iStyle = [ ];

        //  根据断点分析,上面能正常取到 含伪类/伪元素的样式规则,但下面就开始崩溃……

        for (var i = 0;  i < iStyleSheet.length;  i++)
            if ($.inArray(iStyleSheet[i].pseudo, 'before') > -1)
                iStyle.push(iStyleSheet[i].style);

        $(this).change(function () {
            var iPercent = ((this.value / this.max) * 100) + '%';

            for (var i = 0;  i < iStyle.length;  i++)
                iStyle[i].setProperty('width', iPercent, 'important');
        });
    }

    //  目前的笨办法 —— 直接屏蔽低级内核……
    Pseudo_Bind.No_Bug = (Math.floor($.browser.webkit) > 533);

    $.fn.Range = function () {
        return  this.each(function () {
                var $_This = $(this);

                //  Fill-Lower for Gecko and WebKit
                if (Pseudo_Bind.No_Bug && (! $_This.hasClass('Detail')))
                    $_This.cssRule({
                        ':before': {
                            width:    (($_This[0].value / $_This[0].max) * 100) + '%  !important'
                        }
                    }, Pseudo_Bind);
            });
    };

})(self,  self.document,  self.jQuery || self.Zepto);

万万没想到 —— 某些移动版 WebKit 内核浏览器(微信、UC 9.8 等)一到处理 $.fn.cssRule() 回传样式表对象的回调函数 就崩溃(整个 App 退出,UC 重进后提示“未正常关闭,是否恢复上次浏览”)……

MIUI 自带浏览器桌面端 Chrome 一切正常,它们都是 WebKit v537+;苹果机的 WebView 都跟着 iOS 8 升级到了 WebKit v600,也一切正常~

加载中
返回顶部
顶部