jQuery源码阅读10:浏览器检测support()

suppor()t用html和css操作来验证浏览器特性,而不是直接查userAgent
里边提到的好些问题我都不知道, 这篇文章也就是个摘抄, 最多算个翻译…
粘一遍注释学习一下吧

源码

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
292
293
294
295
296
297
298
299
jQuery.support = (function() {
var support,
all,
a,
select,
opt,
input,
fragment,
eventName,
i,
isSupported,
clickFn,
div = document.createElement("div");//创建一个测试用的div
//class值,只有ie67能写进去
div.setAttribute( "className", "t" );
//插入测试innerHtml
//头一个空格ie会去掉
//<table>中ie会自动填上<tbody>
//href里的'/a'会被ie去掉
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
//获取刚刚创建的全部
all = div.getElementsByTagName("*");
a = div.getElementsByTagName("a")[ 0 ];
if ( !all || !a || !all.length ) {
//这都没有就不用继续测了
return {};
}
// First batch of tests
//创建select 里边插入一个option
select = document.createElement("select");
opt = select.appendChild( document.createElement("option") );
input = div.getElementsByTagName("input")[ 0 ];
// cssText:设置样式
// 说明:在IE中最后一个分号会被删掉,可以添加一个分号来解决这个问题
// 例如: Element.style.cssText += ’;width:100px;height:100px;top:100px;left:100px;’
a.style.cssText = "top:1px;float:left;opacity:.5";
//创建测试结果对象
support = {
// 检测节点类型是否为文本节点 成功返回true,失败返回false
// 使用innerHTML,IE会自动剔除HTML代码头部的空白符
leadingWhitespace: ( div.firstChild.nodeType === 3 ),
// 验证是否存在tbody标签,不存在返回true,存在返回false
// 确保tbody元素不会自动插入(IE6~7会在<table></table>自动插入的空tbody)
tbody: !div.getElementsByTagName("tbody").length,
// 验证innerHTML插入链接元素是否可被序列化,成功则返回true,失败返回false
// 所谓序列化是指:可被读取的一种存储标准,在IE6~8中返回false。
htmlSerialize: !!div.getElementsByTagName("link").length,
// 验证getAttribute("style")是否返回元素的行内样式,成功返回true,失败返回false
// 在IE6~8中为false,因为他用cssText代替
style: /top/.test( a.getAttribute("style") ),
// 验证getAttribute("href")返回值是否原封不动,成功返回true,失败返回false
// 在IE6~7中会返回false,因为他的URL已常规化。
hrefNormalized: ( a.getAttribute("href") === "/a" ),
// 验证浏览器是否能正确解析opacity,成功返回true,失败返回false
// 在IE6~8中返回false,因为他用alpha滤镜代替。
opacity: /^0.5/.test( a.style.opacity ),
// 验证cssFloat是否存在,成功返回true,失败返回false
// 在IE6~8中返回false,他用styleFloat代替
cssFloat: !!a.style.cssFloat,
// 验证checkbox的默认value是否为'on',成功返回true,失败返回false
// WebKit默认为'' 空字符串
checkOn: ( input.value === "on" ),
// 验证创建的select元素的第一个option元素是否会默认选中, 成功返回true,失败返回false
// FF,Chrome返回true,IE6~10返回false
// 注意:option元素的父元素不一定是select,也有可能是optgroup
optSelected: opt.selected,
// 验证一些属性是否支持使用get/setAttribute方法,不支持返回true,支持返回false
// 通过setAttribute添加class值是否成功来检测,IE6~7支持,其他浏览器不支持
getSetAttribute: div.className !== "t",
// 验证创建form的enctype属性是否存在,存在返回true,不存在返回fasle(IE6~9,均存在)
// enctype:设置表单的MIME编码,默认编码格式是application/x-www-form-urlencoded,不能用于文件上传;multipart/form-data,才能完整的传递文件数据
enctype: !!document.createElement("form").enctype,
// 验证是否支持html5节点复制,成功返回ture,失败返回false
// 失败:复制节点cloneNode(true).innerHTML返回一个空字符串
html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
// 验证页面和浏览器是否以W3C CSS盒式模型来渲染,而非怪异模式下。IE6~7的怪癖模式会返回false
boxModel: ( document.compatMode === "CSS1Compat" ),
//这些值一句话两句话判断不过来, 之后的代码会修改
submitBubbles: true,
changeBubbles: true,
focusinBubbles: false,
deleteExpando: true,
noCloneEvent: true,
inlineBlockNeedsLayout: false,
shrinkWrapBlocks: false,
reliableMarginRight: true,
boxSizingReliable: true,
pixelPosition: false
};
// 检测单选框选中状态能否正确克隆
// 在IE6~9中会返回false,无法正确克隆
// (1) 设置checkbox的checked为true
input.checked = true;
// (2) cloneNode克隆(复制)一份checkbox,获取他的checked值
support.noCloneChecked = input.cloneNode( true ).checked;
// 检测select元素设置为disabled后,其所有option子元素是否也会被设置为disabled
// (1)禁用下拉列表
select.disabled = true;
// (2)获取下拉列表子元素的disabled是否为true
// 测试:IE FF Chrome Safair Opera 的opt.disabled都为false,说明option不会被设置为disabled
// 其他:部分webkit会被设置为disabled,需要老版本的chrome支持。
support.optDisabled = !opt.disabled;
// Support: IE<9
// 检测是否能删除附加在DOM Element 上的属性或数据
// 在IE6~7中返回false,若事先声明这个属性,那么在IE8返回true,否者返回false
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}
if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
div.attachEvent( "onclick", clickFn = function() {
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
support.noCloneEvent = false;
});
div.cloneNode( true ).fireEvent("onclick");
div.detachEvent( "onclick", clickFn );
}
// 检测input元素被设置为radio类型后,是否仍然保持原先的值 保持成功返回true,失败返回false
// 在IE6~9和opera中返回false,其他返回true
input = document.createElement("input");
input.value = "t";
input.setAttribute( "type", "radio" );
//IE返回on
support.radioValue = input.value === "t";
input.setAttribute( "checked", "checked" );
// 先“选中”然后在“名称”,顺序要点,在老版本的chroem16和safair 下有兼容问题
input.setAttribute( "name", "t" );
div.appendChild( input );
fragment = document.createDocumentFragment();
fragment.appendChild( div.lastChild );
// 检测fragment中的checkbox的选中状态能否被复制 成功返回true,失败返回false
// 在IE6~7中 失败返回false
// 其他:(1) safair下 若先设置"名称"后"选中"返回true
// 其他:(2) safair下 若先设置"选中"后"名称"返回false
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
// 检测(使用setAttribute)被添加到DOM中的checkbox是否仍然保留原先的选中状态 成功返回true,失败返回false
// 在IE6~7中,返回false
// 其他:(1) safair下 若先未设置"名称",返回true
// 其他:(2) safair下 若设置"名称",则返回false
support.appendChecked = input.checked;
fragment.removeChild( input );
fragment.appendChild( div );
// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
// 检测克隆 DOM Element 是否会连同Element一起克隆(复制),成功返回false,失败返回true
// IE6~8克隆会复制事件,标准浏览器返回false
if ( div.attachEvent ) {
for ( i in {
submit: true,
change: true,
focusin: true
}) {
eventName = "on" + i;
isSupported = ( eventName in div );
if ( !isSupported ) {
div.setAttribute( eventName, "return;" );
isSupported = ( typeof div[ eventName ] === "function" );
}
support[ i + "Bubbles" ] = isSupported;
}
}
// 页面加载完成之后开始执行
jQuery(function() {
var container, div, tds, marginDiv,
// 声明保存一种样式(目的是重置样式)
divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
body = document.getElementsByTagName("body")[0];
if ( !body ) {
// 框架的页面没有body元素,返回空值
return;
}
//创建dom,设置css
container = document.createElement("div");
container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
body.insertBefore( container, body.firstChild );
// Construct the test element
div = document.createElement("div");
container.appendChild( div );
// (only IE 8 fails this test)
// 插入table表格
div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
// 获取TD
tds = div.getElementsByTagName("td");
tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
// 检测none状态下,offsetHeight值是否正确,正确返回true,失败返回false
// 在IE6~8下返回false
isSupported = ( tds[ 0 ].offsetHeight === 0 );
// 检测TD初始化,相邻的td的隐藏,offsetHeight值是否正确,正确返回true,失败返回false
// 在IE6~8返回false
// 设置TD[0]display(显示)初始化,相邻的td隐藏
tds[ 0 ].style.display = "";
tds[ 1 ].style.display = "none";
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
// Check box-sizing and margin behavior
div.innerHTML = "";
div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
// 检测是否符盒模型(通过offsetWidth来判断)
// 在IE6~7中返回false 返回值为6
support.boxSizing = ( div.offsetWidth === 4 );
support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
// 未来判断(向上检测)
// 判断是否支持getComputedStyle方法
// getComputedStyle:获取当前元素所有最终使用的CSS属性值,返回的是一个CSS样式声明对象
if ( window.getComputedStyle ) {
// 检测图层定位(像素位置)是否有误,正确返回false 错误返回true
support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
// 检测盒模型是否可靠
support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
// 为处理某个BUG存在,块级元素margin right兼容问题
// webkit 返回错误的值
// 创建一个div元素
marginDiv = document.createElement("div");
// 样式声明或(重置)
marginDiv.style.cssText = div.style.cssText = divReset;
marginDiv.style.marginRight = marginDiv.style.width = "0";
div.style.width = "1px";
div.appendChild( marginDiv );
support.reliableMarginRight =
!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
}
// FF不支持zoom 检测IE6~7 版本
if ( typeof div.style.zoom !== "undefined" ) {
// Check if natively block-level elements act like inline-block
// elements when setting their display to 'inline' and giving
// them layout
// (IE < 8 does this)
div.innerHTML = "";
div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
// 检测IE6~7下,块元素在display:inline并拥有layout属性,是否会按inline-block显示
support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
// 检测父元素拥有layout属性和固定的width/height,是否会被子元素撑大。成功返回true失败,返回false
// 在IE6中返回true,(值是7)
div.style.display = "block";
div.style.overflow = "visible";
div.innerHTML = "<div></div>";
div.firstChild.style.width = "5px";
support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
container.style.zoom = 1;
}
//删除对象, 释放内存
body.removeChild( container );
container = div = tds = marginDiv = null;
});
// IE下,创建DOM Element对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的
// 不需要的DOM Element,需要做结束清理
// 释放dom元素占用的内存
// 释放DOM Element对象,垃圾清理,内存回收
fragment.removeChild( div );
all = a = select = opt = input = fragment = div = null;
return support;
})();
}