html5shiv.js分析

首先,我们先了解一下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

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值