細(xì)說瀏覽器特性檢測(1)-jQuery1.4添加部分
function addEvent(element, type, handler) {
if (element.attachEvent) { //IE8及以下瀏覽器
element.attachEvent('on' + type, handler);
}
else { //W3C標(biāo)準(zhǔn)瀏覽器
element.addEventListener(type, handler, false);
}
};
函數(shù)可以通過檢測attachEvent函數(shù)是否存在,以決定使用attachEvent或者addEventListener,這也是最簡單的一種特性檢測,因而通常在需要時才進(jìn)行實時的檢測。另一種特性檢測由于檢測的過程較為麻煩,因此會預(yù)先完成檢測,將檢測的結(jié)果(通常是Boolean類型)保存在某個變量中。
本文的主要目標(biāo)是分析、說明在jQuery1.4中瀏覽器特性檢測新增的內(nèi)容,同時加深瀏覽器兼容性方面幾個細(xì)節(jié)的記憶。
jQuery1.4主要增加了以下幾個瀏覽器特性標(biāo)識,本文針對它們一一進(jìn)行分析:
- checkOn
- 1.4版本引入,決定沒有設(shè)置value值的checkbox是否有默認(rèn)的value值”on”。
- optSelected
- 1.4.3版本引入,決定select元素的第一個option元素是否會默認(rèn)被選中。
- optDisabled
- 1.4.3版本引入,決定當(dāng)select元素設(shè)置為disabled后,其所有option子元素是否也會被設(shè)置為disabled。
- checkClone
- 1.4.1版本引入,決定對DocumentFragment使用cloneNode函數(shù)時是否會將radio和checkbox的checked屬性保留。
- inlineBlockNeedsLayout
- 1.4.3版本引入,決定在IE下一個block元素?fù)碛衕asLayout屬性并有
display: inline;時,是否會按inline-block顯示。 - shrinkWrapBlocks
- 1.4.3版本引入,決定在IE下一個元素?fù)碛衕asLayout屬性和固定的width/height時,是否不會被子元素?fù)未蟆?
- reliableHiddenOffsets
- 1.4.3版本引入,決定一個td或th元素設(shè)置為
display: none;時,是否還有offsetHeight。
checkOn
使用以下代碼可以檢測該特性:
<input id="checkOn" type="checkbox" />
<script type="text/javascript">
alert(document.getElementById('checkOn').value);
</script>
以下為各瀏覽器中運行結(jié)果:
| IE6 | on |
| IE7 | on |
| IE8 | on |
| IE9 beta | on |
| Firefox 3.6 | on |
| Chrome 7 | [空字符串] |
| Safari 5 | on |
經(jīng)測試,除Chrome外,所有瀏覽器都會給沒有value的checkbox一個默認(rèn)的value值”on”。
該特性被jQuery用來獲取checkbox和radio的值,兼容的判斷語句如下:
//不支持checkOn的瀏覽器都不存在property/attribute混用問題,因此需要明確使用getAttribute
return support.checkOn ?
element.value :
(element.getAttribute('value') === null ? 'on' : element.value);
optSelected
使用以下代碼可以檢測該特性:
<select id="optSelected">
</select>
<script type="text/javascript">
var select = document.getElementById('optSelected'),
option = document.createElement('option');
select.appendChild(option);
alert(option.selected);
</script>
以下為各瀏覽器中運行結(jié)果:
| IE6 | false |
| IE7 | false |
| IE8 | false |
| IE9 beta | false |
| Firefox 3.6 | true |
| Chrome 7 | true |
| Safari 5 | false |
經(jīng)測試,IE系列和Safari使用appendChild對空的select元素添加一個option后,該option的selected屬性不會被默認(rèn)設(shè)置為true。
該問題引起的BUG描述如下:
部分瀏覽器在獲取option的selected屬性時,會錯誤地返回false。
該問題的解決方案是在訪問selected屬性時,先訪問其父級select元素的selectedIndex屬性,強(qiáng)迫瀏覽器計算option的selected屬性,以得到正確的值。需要注意的是option元素的父元素不一定是select,也有可能是optgroup。具體代碼如下:
if (!support.optSelected) {
var parent = option.parentNode;
parent.selectedIndex;
//處理optgroup時的情況
if (parent.parentNode) {
parent.parentNode.selectedIndex;
}
}
return option.selected;
optDisabled
使用以下代碼可以檢測該特性:
<select id="optDisabled" disabled="disabled">
<option></option>
</select>
<script type="text/javascript">
var select = document.getElementById('optDisabled'),
option = select.getElementsByTagName('option')[0];
alert(option.disabled);
</script>
以下為各瀏覽器中運行結(jié)果:
| IE6 | false |
| IE7 | false |
| IE8 | false |
| IE9 beta | false |
| Firefox 3.6 | false |
| Chrome 7 | false |
| Safari 5 | true |
經(jīng)測試,Safari會將設(shè)置了disabled的select中的option也同樣設(shè)置上disabled。
這個特性用來獲取select元素的value值,特別是當(dāng)select渲染為多選框時,需要注意從中去除disabled的option元素,但在Safari中,獲取被設(shè)置為disabled的select的值時,由于所有option元素都被設(shè)置為disabled,會導(dǎo)致無法獲取值。
因此有optDisabled(true表示option不會被自動設(shè)置disabled)后,可以有這樣的代碼:
//如果optDisabled為true,則disabled屬性返回的是option的真實狀態(tài)
//否則判斷disabled屬性是否為null
var disabled = support.optDisabled ?
option.disabled : option.getAttribute('disabled') !== null;
if (!disabled) {
return option.value;
}
checkClone
使用以下代碼可以檢測該特性:
<div id="checkClone">
<input type="radio" name="checkClone" checked="checked" />
</div>
<script type="text/javascript">
var fragment = document.createDocumentFragment(),
div = document.getElementById('checkClone'),
radio = div.getElementsByTagName('input')[0];
fragment.appendChild( radio );
alert(fragment.cloneNode(true).cloneNode(true).lastChild.checked);
</script>
需要注意的是,重現(xiàn)這個問題,需要給input顯式地指定一個name屬性,并且在復(fù)制fragment對象時連續(xù)調(diào)用2次cloneNode函數(shù)。
以下為各瀏覽器中運行結(jié)果:
| IE6 | true |
| IE7 | true |
| IE8 | true |
| IE9 beta | true |
| Firefox 3.6 | true |
| Chrome 7 | true |
| Safari 5 | true |
| Safari 4 | false |
由結(jié)果可以看出,該問題出現(xiàn)在Safari 4中,并且已經(jīng)在Safari 5得到修復(fù),介于Safari在市場中的占有率以及版本較老的原因,這個問題確實不需要太多的重視。
這個特性的使用場合極少,在開發(fā)中幾乎不會有如此嚴(yán)格的環(huán)境(對DocumentFragment連續(xù)調(diào)用2次cloneNode),在jQuery中,該特性用做buildFragment這個內(nèi)部函數(shù)中的緩存功能,jQuery會對比較簡單的創(chuàng)建DOM元素的字符串的創(chuàng)建結(jié)果緩存到DocumentFragment中,但當(dāng)遇到創(chuàng)建radio時,如果cloneNode為false,則強(qiáng)制不進(jìn)行緩存。
inlineBlockNeedsLayout
這是一個歷史久遠(yuǎn)的問題,IE7以下版本并不支持display: inline-block;樣式,而是使用display: inline;并通過其他樣式觸發(fā)其hasLayout形成一種偽inline-block的狀態(tài)(具體請點擊這里)。
inline-block與inline的一個重要區(qū)別在于,inline-block的元素可以顯式地設(shè)置寬和高,因此可以用以下代碼檢測該特性:
<div id="inlineBlockNeedsLayout" style="width: 1px; padding-left: 1px; display: inline;
zoom: 1;">
</div>
<script type="text/javascript">
var div = document.getElementById('inlineBlockNeedsLayout');
alert(div.offsetWidth);
</script>
以下為各瀏覽器中運行結(jié)果:
| IE6 | 2 |
| IE7 | 2 |
| IE8 | 1 |
| IE9 beta | 1 |
| Firefox 3.6 | 1 |
| Chrome 7 | 0 |
| Safari 5 | 0 |
對于inline元素,width樣式是無效的,在該測試中,webkit系瀏覽器均獲取了0,IE8以上版本及Firefox獲取了1,只有IE7及以下版本同時計算了width和padding-left,得到了2px的寬度。
這個功能可以用于設(shè)置元素的css樣式,當(dāng)需要設(shè)置為inline-block時,針對IE7及以下瀏覽器可以同時設(shè)置display: inline;和zoom: 1;來模擬效果,核心代碼如下:
if (name == 'display' && value == 'inline-block') {
if (support.inlineBlockNeedsLayout) {
element.style.display = 'inline';
element.style.zoom = 1;
}
else {
element.style.display = value;
}
};
當(dāng)然這樣直接這樣使用肯定是有問題的,當(dāng)需要獲取display樣式的時候怎么辦呢?同時判斷zoom和display嗎?并且hasLayout會引起一些其他的問題。
因此,jQuery只將該特性用于動畫效果,當(dāng)需要對width和height進(jìn)行動畫,并且元素是inline時,首先設(shè)置為(偽)inline-block狀態(tài),動畫結(jié)束后將相關(guān)樣式恢復(fù)。
shrinkWrapBlocks
這個問題的詳細(xì)解釋可以參考此處,使用以下代碼可以檢測該特性:
<div id="shrinkWrapBlocks" style="width: 1px; zoom: 1;">
<div style="width: 4px;">
</div>
</div>
<script type="text/javascript">
var div = document.getElementById('shrinkWrapBlocks'),
inner = div.getElementsByTagName('div')[0];
alert(div.offsetWidth);
</script>
以下為各瀏覽器中運行結(jié)果:
| IE6 | 4 |
| IE7 | 1 |
| IE8 | 1 |
| IE9 beta | 1 |
| Firefox 3.6 | 1 |
| Chrome 7 | 1 |
| Safari 5 | 1 |
測試結(jié)果表明,IE6即使顯式設(shè)定了寬度,在觸發(fā)了hasLayout的情況下,其大小會受子元素的影響而被撐大。
jQuery將該特性用于動畫效果,為了動畫過程中改變一個元素的width/height時,其子元素不會溢出,jQuery做了以下幾步:
- 保存元素當(dāng)前的overflow、overflow-x、overflow-y三個樣式。
- 將元素設(shè)置為inline-block以便修改width/height值。
- 將元素的overflow設(shè)為hidden,防止子元素溢出或當(dāng)前元素被子元素?fù)伍_(IE6)。
- 在動畫結(jié)束后,確保元素不會被子元素?fù)伍_(shrinkWrapBlocks為true)的情況下,才恢復(fù)overflow樣式。
reliableHiddenOffsets
這個問題在上兩天工作中遇到,剛好jQuery1.4.3升級了這方面的內(nèi)容,使用以下代碼可以檢測該特性:
<table id="reliableHiddenOffsets">
<tbody>
<tr>
<td style="display: none;">
</td>
<td>
abcd
</td>
</tr>
</tbody>
</table>
<script type="text/javascript">
var table = document.getElementById('reliableHiddenOffsets'),
td = table.getElementsByTagName('td')[0];
alert(td.offsetHeight);
</script>
以下為各瀏覽器中運行結(jié)果:
| IE6 | 0 |
| IE7 | 0 |
| IE8 | 21 |
| IE9 beta | 0 |
| Firefox 3.6 | 0 |
| Chrome 7 | 0 |
| Safari 5 | 0 |
只有IE8存在這個問題,那當(dāng)td元素的display為none時,其高度依舊會受其所在行的高度的影響,而不是0。
這個問題的存在根本上導(dǎo)致了對元素可見性的判定出現(xiàn)差錯,原本判斷一個元素是否隱藏的代碼是這樣的:
function isHidden(element) {
return element.offsetWidth == 0 || element.offsetHeight == 0;
};
因為這個BUG的出現(xiàn),上面的函數(shù)對于td元素失去了效果,因此需要改進(jìn)為:
function isVisible(element) {
return (element.offsetWidth == 0 && element.offsetHeight == 0) ||
(!support.reliableHiddenOffsets && getStyle(element, 'display') == 'none');
};
閱讀jQuery源碼的時候,會發(fā)現(xiàn)這一段的判斷里多了一句element.style.display,這一句是用來判斷元素有display值才去取來看看是不是none的,以免獲取運行時樣式的開銷。
結(jié)語
- 特性檢測確實很有用,有時比瀏覽器版本嗅探更佳可靠,但檢測某些特性相當(dāng)麻煩,不是必要的時候不如用瀏覽器嗅探。
- jQuery對特性的命名真讓人想砍了他們團(tuán)隊。
- 有些特性可以重現(xiàn)的瀏覽器版本之低令人驚訝,在多數(shù)項目中完全可以不考慮,如checkClone。jQuery本身為了兼容做了太多的假設(shè),個人認(rèn)為有一些完全可以拋棄,比如以后會說的getBoundingClientRect問題。
- 另外還有2個關(guān)于事件上的特性檢測,由于事件的特性檢測是一個通用的話題,會有今后專門寫文講述,因此就不在本文中贅述了。
- jQuery每一個小版本的改進(jìn)都很大,特別在細(xì)節(jié)方面,這些都是要通過閱讀源碼不斷發(fā)掘的,前端的世界就是這么多變(嘆)。
- 本文所用的示例可以點此查看,具體可以查看源代碼,本文所述的各個問題/BUG都沒在網(wǎng)上找到比較權(quán)威的說明,還請見諒!
[Ctrl+A 全選 注:引入外部Js需再刷新一下頁面才能執(zhí)行]
相關(guān)文章
淺談jQuery中ajaxPrefilter的應(yīng)用
下面小編就為大家?guī)硪黄獪\談jQuery中ajaxPrefilter的應(yīng)用。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08
Jquery原生態(tài)實現(xiàn)表格header頭隨滾動條滾動而滾動
表頭是浮動的,因為內(nèi)容在同一頁面展示,當(dāng)滾動時,看不到列頭,為了改動少只能使用jquery原生態(tài)實現(xiàn)滾動2014-03-03
js判斷checkbox是否選中個數(shù)的方法(超簡單)
下面小編就為大家?guī)硪黄猨s判斷checkbox是否選中個數(shù)的方法(超簡單)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08

