• html5shiv.js分析-读源码之javascript系列


     

    xiaolingzi 发表于 2012-05-31 23:42:29

    首先,我们先了解一下html5shiv.js是什么。

    html5shiv.js是一套实现让ie低版本等浏览器支持html5标签的解决方案。

    实现原理:见如何让ie低版本浏览器支持html5标签 。

    废话不多说,我们先上源代码,代码有点长,但保持原来的注释有利于大家理解,不想直接阅读的就点收缩代码然后往下看。源码原地址:https://github.com/aFarkas/html5shiv 。

    -收缩代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    /*! HTML5 Shiv vpre3.6 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed */
    ;(function(window, document) {
                                         
      /** Preset options */
      var options = window.html5 || {};
                                         
      /** Used to skip problem elements */
      var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup
    )$/i;
                                         
      /** Not all elements can be cloned in IE (this list can be shortend) **/
      var saveClones = /^<(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|
    i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|
    strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i;
                                         
      /** Detect whether the browser supports default html5 styles */
      var supportsHtml5Styles;
                                         
      /** Name of the expando, to work with multiple documents or to re-shiv one document */
      var expando = '_html5shiv';
                                         
      /** The id for the the documents expando */
      var expanID = 0;
                                         
      /** Cached data for each document */
      var expandoData = {};
                                         
      /** Detect whether the browser supports unknown elements */
      var supportsUnknownElements;
                                         
      (function() {
        var a = document.createElement('a');
                                         
        a.innerHTML = '<xyz></xyz>';
                                         
        //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
        supportsHtml5Styles = ('hidden' in a);
                                         
        supportsUnknownElements = a.childNodes.length == 1 || (function() {
          // assign a false positive if unable to shiv
          try {
            (document.createElement)('a');
          catch(e) {
            return true;
          }
          var frag = document.createDocumentFragment();
          return (
            typeof frag.cloneNode == 'undefined' ||
            typeof frag.createDocumentFragment == 'undefined' ||
            typeof frag.createElement == 'undefined'
          );
        }());
                                         
      }());
                                         
      /*--------------------------------------------------------------------------*/
                                         
      /**
    * Creates a style sheet with the given CSS text and adds it to the document.
    * @private
    * @param {Document} ownerDocument The document.
    * @param {String} cssText The CSS text.
    * @returns {StyleSheet} The style element.
    */
      function addStyleSheet(ownerDocument, cssText) {
        var p = ownerDocument.createElement('p'),
            parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
                                         
        p.innerHTML = 'x<style>' + cssText + '</style>';
        return parent.insertBefore(p.lastChild, parent.firstChild);
      }
                                         
      /**
    * Returns the value of `html5.elements` as an array.
    * @private
    * @returns {Array} An array of shived element node names.
    */
      function getElements() {
        var elements = html5.elements;
        return typeof elements == 'string' ? elements.split(' ') : elements;
      }
                                           
        /**
    * Returns the data associated to the given document
    * @private
    * @param {Document} ownerDocument The document.
    * @returns {Object} An object of data.
    */
      function getExpandoData(ownerDocument) {
        var data = expandoData[ownerDocument[expando]];
        if (!data) {
            data = {};
            expanID++;
            ownerDocument[expando] = expanID;
            expandoData[expanID] = data;
        }
        return data;
      }
                                         
      /**
    * returns a shived element for the given nodeName and document
    * @memberOf html5
    * @param {String} nodeName name of the element
    * @param {Document} ownerDocument The context document.
    * @returns {Object} The shived element.
    */
      function createElement(nodeName, ownerDocument, data){
        if (!ownerDocument) {
            ownerDocument = document;
        }
        if(supportsUnknownElements){
            return ownerDocument.createElement(nodeName);
        }
        data = data || getExpandoData(ownerDocument);
        var node;
                                         
        if (data.cache[nodeName]) {
            node = data.cache[nodeName].cloneNode();
        else if (saveClones.test(nodeName)) {
            node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
        else {
            node = data.createElem(nodeName);
        }
                                         
        // Avoid adding some elements to fragments in IE < 9 because
        // * Attributes like `name` or `type` cannot be set/changed once an element
        // is inserted into a document/fragment
        // * Link elements with `src` attributes that are inaccessible, as with
        // a 403 response, will cause the tab/window to crash
        // * Script elements appended to fragments will execute when their `src`
        // or `text` property is set
        return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;
      }
                                         
      /**
    * returns a shived DocumentFragment for the given document
    * @memberOf html5
    * @param {Document} ownerDocument The context document.
    * @returns {Object} The shived DocumentFragment.
    */
      function createDocumentFragment(ownerDocument, data){
        if (!ownerDocument) {
            ownerDocument = document;
        }
        if(supportsUnknownElements){
            return ownerDocument.createDocumentFragment();
        }
        data = data || getExpandoData(ownerDocument);
        var clone = data.frag.cloneNode(),
            i = 0,
            elems = getElements(),
            l = elems.length;
        for(;i<l;i++){
            clone.createElement(elems[i]);
        }
        return clone;
      }
                                         
      /**
    * Shivs the `createElement` and `createDocumentFragment` methods of the document.
    * @private
    * @param {Document|DocumentFragment} ownerDocument The document.
    * @param {Object} data of the document.
    */
      function shivMethods(ownerDocument, data) {
        if (!data.cache) {
            data.cache = {};
            data.createElem = ownerDocument.createElement;
            data.createFrag = ownerDocument.createDocumentFragment;
            data.frag = data.createFrag();
        }
                                         
                                         
        ownerDocument.createElement = function(nodeName) {
          //abort shiv
          if (!html5.shivMethods) {
              return data.createElem(nodeName);
          }
          return createElement(nodeName);
        };
                                         
        ownerDocument.createDocumentFragment = Function('h,f''return function(){' +
          'var n=f.cloneNode(),c=n.createElement;' +
          'h.shivMethods&&(' +
            // unroll the `createElement` calls
            getElements().join().replace(/w+/g, function(nodeName) {
              data.createElem(nodeName);
              data.frag.createElement(nodeName);
              return 'c("' + nodeName + '")';
            }) +
          ');return n}'
        )(html5, data.frag);
      }
                                         
      /*--------------------------------------------------------------------------*/
                                         
      /**
    * Shivs the given document.
    * @memberOf html5
    * @param {Document} ownerDocument The document to shiv.
    * @returns {Document} The shived document.
    */
      function shivDocument(ownerDocument) {
        if (!ownerDocument) {
            ownerDocument = document;
        }
        var data = getExpandoData(ownerDocument);
                                         
        if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
          data.hasCSS = !!addStyleSheet(ownerDocument,
            // corrects block display not defined in IE6/7/8/9
            'article,aside,figcaption,figure,footer,header,hgroup,nav
    ,section{display:block}' +
            // adds styling not present in IE6/7/8/9
            'mark{background:#FF0;color:#000}'
          );
        }
        if (!supportsUnknownElements) {
          shivMethods(ownerDocument, data);
        }
        return ownerDocument;
      }
                                         
      /*--------------------------------------------------------------------------*/
                                         
      /**
    * The `html5` object is exposed so that more elements can be shived and
    * existing shiving can be detected on iframes.
    * @type Object
    * @example
    *
    * // options can be changed before the script is included
    * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
    */
      var html5 = {
                                         
        /**
    * An array or space separated string of node names of the elements to shiv.
    * @memberOf html5
    * @type Array|String
    */
        'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video',
                                         
        /**
    * A flag to indicate that the HTML5 style sheet should be inserted.
    * @memberOf html5
    * @type Boolean
    */
        'shivCSS': !(options.shivCSS === false),
                                         
        /**
    * Is equal to true if a browser supports creating unknown/HTML5 elements
    * @memberOf html5
    * @type boolean
    */
        'supportsUnknownElements': supportsUnknownElements,
                                         
        /**
    * A flag to indicate that the document's `createElement` and `createDocumentFragment`
    * methods should be overwritten.
    * @memberOf html5
    * @type Boolean
    */
        'shivMethods': !(options.shivMethods === false),
                                         
        /**
    * A string to describe the type of `html5` object ("default" or "default print").
    * @memberOf html5
    * @type String
    */
        'type': 'default',
                                         
        // shivs the document according to the specified `html5` object options
        'shivDocument': shivDocument,
                                         
        //creates a shived element
        createElement: createElement,
                                         
        //creates a shived documentFragment
        createDocumentFragment: createDocumentFragment
      };
                                         
      /*--------------------------------------------------------------------------*/
                                         
      // expose html5
      window.html5 = html5;
                                         
      // shiv the document
      shivDocument(document);
                                         
    }(this, document));

    代码结构分析:

    1.整个代码放在一个匿名函数里面并执行,该匿名使用的模式如下:

    (function{}())

    受到作用域的限制,执行时将当前window(this)和document作为参数传递进去。关于匿名函数执行形式的相关说明请参考之前的文章javascript匿名函数的各种执行形式

    2.我们从上往下依次浏览一下代码,并将代码划分为五部分。

    第一部分是从开始到第30行。

    第二部分是从第31行到55行。

    第三部分是从56行到234行。

    第四部分是从235行到278行。

    第五部分是剩下部分。

    好,那么下面我们就从这五部分中分别找出我们可以学习的一些知识点。

    第一部分:该部分主要是私有变量的定义,学习到的知识点有:

    1.了解到不是所有元素都可以在IE中进行复制,具体参看saveClones 的定义和上面的注释。

    2.了解到可以通过一对空大括号{}对一对象进行初始化,见expandoData的定义。

    3.了解到变量在开始集中定义的习惯。因为在javascript中,就算将变量定义在其他地方也会预先执行定义,所以可以集中在前面一起定义,这样也有利于变量的管理。

    第二部分执行一个匿名函数来检测浏览器对html5中的css和未知标签的支持情况,并保存结果。学到的知识点有:

    1.如何检测浏览器对html5的样式的支持。此处的思路是通过定义一个超链接元素a,然后检测在当前浏览器中a元素是否具备hidden属性,hidden为html5中新增的一个属性,使用该属性可以对元素进行隐藏。

    判断的代码如下:

    supportsHtml5Styles = ('hidden' in a);

    对各浏览器的测试结果如下:

    浏览器 支持情况
    Chrome(18.0.1025.168 m) true
    FireFox(12.0) true
    Safari(5.1.7) true
    Opera(11.64) true
    IE9 false
    IE8 false
    IE7 false

    2.如何检测浏览器对未知元素的支持情况。此处的思路是,给定义的a元素填充一未知元素,然后检测a的子元素的个数,若等于1则表示支持未知元素,否则不支持。其次通过检查一个错位执行的支持情况(document.createElement)('a');,但个人测试各浏览器都通过,不知道作者检查是哪些浏览器。最后才通过对文档碎片节点的一些方法的支持情况来进行判断。

    对各浏览器的测试结果如下:

     

    a.childNodes.
    length

    (document.createElement)('a') frag.cloneNode frag.createDocumentFragment

    frag.
    createElement

    Chrome(18.0.1025.168 m) 1 通过 defined undefined undefined
    FireFox(12.0) 1 通过 defined undefined undefined
    Safari(5.1.7) 1 通过 defined undefined undefined
    Opera(11.64) 1 通过 defined undefined undefined
    IE9 1 通过 defined undefined undefined
    IE8 0 通过 defined defined defined
    IE7 0 通过 defined defined defined

    第三部分:该部分主要是定义一系列的私有方法。学到的知识点有如下:

    1.了解了个浏览器对lastChild的支持情况。我们来看addStyleSheet方法,该方法的主要作用是将样式添加到页面中。我们留意到在加入style标签的时候前面多加了一个x。为什么要这样子做呢?这是由于在只有一个节点的情况lastChild会出现兼容性问题,主要表现在IE8和IE7无法通过它来获取到那唯一的节点。

    对各浏览器的测试结果如下:

      p.lastChild(不加x) p.lastChild(加x)
    Chrome(18.0.1025.168 m) object HTMLStyleElement object HTMLStyleElement
    FireFox(12.0) object HTMLStyleElement object HTMLStyleElement
    Safari(5.1.7) object HTMLStyleElement object HTMLStyleElement
    Opera(11.64) object HTMLStyleElement object HTMLStyleElement
    IE9 object HTMLStyleElement object HTMLStyleElement
    IE8 null object HTMLStyleElement
    IE7 null object

    2.Function的使用,注意这里是首字母大写的。此处的使用请查看shivMethods方法。Function主要是用来实现动态执行。它可以实现跟eval一样的工作,但由于它在性能方面胜过eval,所以很多人都推荐使用Function。

    Function的执行形式如下:
    var 函数名 = new Function('argument1','argument2',...,'argumentN','函数体');
    或者
    var 函数名 = new Function('argument1,argument2,...,argumentN','函数体');
    或者
    new Function('执行体');

    我们看到上面的形式都使用了new关键字进行实例化,但是我们看到本例源码中却没有new,经过测试发现new关键字可以省略。

    3.createDocumentFragment即创建文档碎片节点的使用。创建文档碎片节点的目的是为了减少浏览器渲染的次数来提升性能。比如,当我们要往页面中添加一系列节点时,如果每次都实时向页面使用appendChild来添加节点时,那么每次浏览器都会渲染一次,而过多次数的渲染就会造成性能问题。如果我们先把要添加的节点都先加到文档碎片节点中去,完成后再一次添加到页面中去就只渲染一次。

    第四部分:该部分主要定义html5对象的一些属性和方法。学到的知识点如下:

    1.通过json的方式进行属性和方法的封装。可以大大减少全局变量的污染。具体没必要再详说。

    2.通过将私有方法或属性赋值给全局对象的属性来将方法公开。比如当我们定义了许多方法或属性,但我们不想公开所有方法或属性,此时就可以通过闭包将方法私有化,然后再通过返回赋值给全局变量的方式公开部分属性和方法。如此处通过名字叫html5的全局对象的属性进行公开。

    第五部分:该部分主要是将html5对象保留给全局window,并执行入口函数。学到的知识点主要是如何在函数中将对象暴露给window(全局化)。

    除了上面说的知识点外,还有一个非常重要的地方就是学习别人优秀的设计模式和架构。

    好吧,文章就到此结束。如有不对之处欢迎指出交流。

    转载请注明出处:http://xxling.com/article/41.aspx

  • 相关阅读:
    微信浏览器内 h5 直接唤醒 app 之 微信开放标签 wx-open-launch-app
    HTML5之2D物理引擎 Box2D for javascript Games 系列 翻外篇--如何结合createJS应用box2d.js
    HTML5之2D物理引擎 Box2D for javascript Games 系列 第三部分之创建图腾破坏者的关卡
    HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分
    HTML5之2D物理引擎 Box2D for javascript Games 系列 第一部分
    写给“有钱大爷”、”美工殿下”、“前端文艺青年”的微信HTML5页面设计建议
    微信 JS-SDK Demo “分享信息设置” API 及数字签名生成方法(NodeJS版本)更新时间(2020-10-29)
    NodeJS让前端与后端更友好的分手
    “榕树下·那年”移动app ( hybrid ) 开发总结
    如何在移动端app中应用字体图标icon fonts
  • 原文地址:https://www.cnblogs.com/aimyfly/p/3425374.html
Copyright © 2020-2023  润新知