DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> jQuery入門知識 >> JQuery特效代碼 >> 細說浏覽器特性檢測(1)-jQuery1.4添加部分
細說浏覽器特性檢測(1)-jQuery1.4添加部分
編輯:JQuery特效代碼     
其最經典的運用莫過於通用的addEvent函數:

function addEvent(element, type, handler) { 
if (element.attachEvent) { //IE8及以下浏覽器 
element.attachEvent('on' + type, handler); 
} 
else { //W3C標准浏覽器 
element.addEventListener(type, handler, false); 
} 
};

函數可以通過檢測attachEvent函數是否存在,以決定使用attachEvent或者addEventListener,這也是最簡單的一種特性檢測,因而通常在需要時才進行實時的檢測。另一種特性檢測由於檢測的過程較為麻煩,因此會預先完成檢測,將檢測的結果(通常是Boolean類型)保存在某個變量中。

本文的主要目標是分析、說明在jQuery1.4中浏覽器特性檢測新增的內容,同時加深浏覽器兼容性方面幾個細節的記憶。

jQuery1.4主要增加了以下幾個浏覽器特性標識,本文針對它們一一進行分析:

checkOn
1.4版本引入,決定沒有設置value值的checkbox是否有默認的value值”on”。
optSelected
1.4.3版本引入,決定select元素的第一個option元素是否會默認被選中。
optDisabled
1.4.3版本引入,決定當select元素設置為disabled後,其所有option子元素是否也會被設置為disabled。
checkClone
1.4.1版本引入,決定對DocumentFragment使用cloneNode函數時是否會將radio和checkbox的checked屬性保留。
inlineBlockNeedsLayout
1.4.3版本引入,決定在IE下一個block元素擁有hasLayout屬性並有display: inline;時,是否會按inline-block顯示。
shrinkWrapBlocks
1.4.3版本引入,決定在IE下一個元素擁有hasLayout屬性和固定的width/height時,是否不會被子元素撐大。
reliableHiddenOffsets
1.4.3版本引入,決定一個td或th元素設置為display: none;時,是否還有offsetHeight。

checkOn

使用以下代碼可以檢測該特性:

<input id="checkOn" type="checkbox" /> 
<script type="text/javascript"> 
alert(document.getElementById('checkOn').value); 
</script>

以下為各浏覽器中運行結果:

IE6 on IE7 on IE8 on IE9 beta on Firefox 3.6 on Chrome 7 [空字符串] Safari 5 on

經測試,除Chrome外,所有浏覽器都會給沒有value的checkbox一個默認的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>

以下為各浏覽器中運行結果:

IE6 false IE7 false IE8 false IE9 beta false Firefox 3.6 true Chrome 7 true Safari 5 false

經測試,IE系列和Safari使用appendChild對空的select元素添加一個option後,該option的selected屬性不會被默認設置為true。

該問題引起的BUG描述如下:

部分浏覽器在獲取option的selected屬性時,會錯誤地返回false。

該問題的解決方案是在訪問selected屬性時,先訪問其父級select元素的selectedIndex屬性,強迫浏覽器計算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>

以下為各浏覽器中運行結果:

IE6 false IE7 false IE8 false IE9 beta false Firefox 3.6 false Chrome 7 false Safari 5 true

經測試,Safari會將設置了disabled的select中的option也同樣設置上disabled。

這個特性用來獲取select元素的value值,特別是當select渲染為多選框時,需要注意從中去除disabled的option元素,但在Safari中,獲取被設置為disabled的select的值時,由於所有option元素都被設置為disabled,會導致無法獲取值。

因此有optDisabled(true表示option不會被自動設置disabled)後,可以有這樣的代碼:

//如果optDisabled為true,則disabled屬性返回的是option的真實狀態 
//否則判斷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>

需要注意的是,重現這個問題,需要給input顯式地指定一個name屬性,並且在復制fragment對象時連續調用2次cloneNode函數。

以下為各浏覽器中運行結果:

IE6 true IE7 true IE8 true IE9 beta true Firefox 3.6 true Chrome 7 true Safari 5 true Safari 4 false

由結果可以看出,該問題出現在Safari 4中,並且已經在Safari 5得到修復,介於Safari在市場中的占有率以及版本較老的原因,這個問題確實不需要太多的重視。

這個特性的使用場合極少,在開發中幾乎不會有如此嚴格的環境(對DocumentFragment連續調用2次cloneNode),在jQuery中,該特性用做buildFragment這個內部函數中的緩存功能,jQuery會對比較簡單的創建DOM元素的字符串的創建結果緩存到DocumentFragment中,但當遇到創建radio時,如果cloneNode為false,則強制不進行緩存。

inlineBlockNeedsLayout

這是一個歷史久遠的問題,IE7以下版本並不支持display: inline-block;樣式,而是使用display: inline;並通過其他樣式觸發其hasLayout形成一種偽inline-block的狀態(具體請點擊這裡)。

inline-block與inline的一個重要區別在於,inline-block的元素可以顯式地設置寬和高,因此可以用以下代碼檢測該特性:

<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>

以下為各浏覽器中運行結果:

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的寬度。

這個功能可以用於設置元素的css樣式,當需要設置為inline-block時,針對IE7及以下浏覽器可以同時設置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; 
} 
};

當然這樣直接這樣使用肯定是有問題的,當需要獲取display樣式的時候怎麼辦呢?同時判斷zoom和display嗎?並且hasLayout會引起一些其他的問題。

因此,jQuery只將該特性用於動畫效果,當需要對width和height進行動畫,並且元素是inline時,首先設置為(偽)inline-block狀態,動畫結束後將相關樣式恢復。

shrinkWrapBlocks

這個問題的詳細解釋可以參考此處,使用以下代碼可以檢測該特性:

<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>

以下為各浏覽器中運行結果:

IE6 4 IE7 1 IE8 1 IE9 beta 1 Firefox 3.6 1 Chrome 7 1 Safari 5 1

測試結果表明,IE6即使顯式設定了寬度,在觸發了hasLayout的情況下,其大小會受子元素的影響而被撐大。

jQuery將該特性用於動畫效果,為了動畫過程中改變一個元素的width/height時,其子元素不會溢出,jQuery做了以下幾步:

  1. 保存元素當前的overflow、overflow-x、overflow-y三個樣式。
  2. 將元素設置為inline-block以便修改width/height值。
  3. 將元素的overflow設為hidden,防止子元素溢出或當前元素被子元素撐開(IE6)。
  4. 在動畫結束後,確保元素不會被子元素撐開(shrinkWrapBlocks為true)的情況下,才恢復overflow樣式。

reliableHiddenOffsets

這個問題在上兩天工作中遇到,剛好jQuery1.4.3升級了這方面的內容,使用以下代碼可以檢測該特性:

<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>

以下為各浏覽器中運行結果:

IE6 0 IE7 0 IE8 21 IE9 beta 0 Firefox 3.6 0 Chrome 7 0 Safari 5 0

只有IE8存在這個問題,那當td元素的display為none時,其高度依舊會受其所在行的高度的影響,而不是0。

這個問題的存在根本上導致了對元素可見性的判定出現差錯,原本判斷一個元素是否隱藏的代碼是這樣的:

function isHidden(element) { 
return element.offsetWidth == 0 || element.offsetHeight == 0; 
};

因為這個BUG的出現,上面的函數對於td元素失去了效果,因此需要改進為:

function isVisible(element) { 
return (element.offsetWidth == 0 && element.offsetHeight == 0) || 
(!support.reliableHiddenOffsets && getStyle(element, 'display') == 'none'); 
};

閱讀jQuery源碼的時候,會發現這一段的判斷裡多了一句element.style.display,這一句是用來判斷元素有display值才去取來看看是不是none的,以免獲取運行時樣式的開銷。

結語

  1. 特性檢測確實很有用,有時比浏覽器版本嗅探更佳可靠,但檢測某些特性相當麻煩,不是必要的時候不如用浏覽器嗅探。
  2. jQuery對特性的命名真讓人想砍了他們團隊。
  3. 有些特性可以重現的浏覽器版本之低令人驚訝,在多數項目中完全可以不考慮,如checkClone。jQuery本身為了兼容做了太多的假設,個人認為有一些完全可以拋棄,比如以後會說的getBoundingClientRect問題。
  4. 另外還有2個關於事件上的特性檢測,由於事件的特性檢測是一個通用的話題,會有今後專門寫文講述,因此就不在本文中贅述了。
  5. jQuery每一個小版本的改進都很大,特別在細節方面,這些都是要通過閱讀源碼不斷發掘的,前端的世界就是這麼多變(歎)。
  6. 本文所用的示例可以點此查看,具體可以查看源代碼,本文所述的各個問題/BUG都沒在網上找到比較權威的說明,還請見諒!


[Ctrl+A 全選 注:如需引入外部Js需刷新才能執行]
XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved