Chapter 9 客戶端檢測
能力檢測
-
用于識(shí)別瀏覽器的能力,基本模式如下
if (object.propertyInQuestion) { // use object.propertyInQuestion }
function getElement(id) { if (document.getElementById) { return document.getElementById(id); } else if (document.all) { return document.all[id]; } else { throw new Error('No way to retrieve element'); } }
-
更可靠的能力檢測
- 確定一個(gè)對象是否支持排序(既要檢測 sort 屬性是否存在察净,也要檢測 sort 屬性是否是一個(gè)函數(shù))
function isSortable(object) { return typeof object.sort == 'function'; }
- 盡量使用 typeof 進(jìn)行能力檢測嗜价。但是在 IE8 之前的版本中迈勋,由于宿主對象是通過 COM 而非 JScript 實(shí)現(xiàn)的烫堤,所以 document.createElement() 之類的函數(shù)是一個(gè) COM 對象陪汽,而非函數(shù)
typeof document.createElement == 'function' // 在 IE8- 中為 false
- IE 中類似的 typeof 的行為不標(biāo)準(zhǔn)的例子還有很多悄晃。
- 在瀏覽器環(huán)境下測試任何對象的某個(gè)屬性是否存在玫霎,要使用下面這個(gè)函數(shù)
function isHostMethod(object, property) { var t = typeof object[property]; return t == 'function' || (!!(t == 'object' && object[property])) || t == 'unknown;
result = isHostMethod(xhr, 'open'); // true
在 JavaScript 中, !! 操作符通常用于將表達(dá)式強(qiáng)制轉(zhuǎn)換為 boolean 類型數(shù)據(jù)妈橄。
-
能力檢測庶近,不是瀏覽器檢測
- 檢測某個(gè)或某幾個(gè)特性并不能夠確定瀏覽器。
- 如果已知應(yīng)用程序需要使用某些特定的瀏覽器特性眷蚓,最好一次檢測所有的相關(guān)特性鼻种,而不是分別檢測。
// 檢測瀏覽器是否具有 DOM1 級(jí)規(guī)定的能力沙热。 var hasDOM1 = !! (document.getElementById && document.createElement && document.getElementByTagName);
怪癖檢測
怪癖檢測的目標(biāo)是識(shí)別瀏覽器的特殊行為叉钥。通過怪癖檢測罢缸,獲知瀏覽器存在什么樣的缺陷。
-
Example: IE8- 中投队,如果某個(gè)實(shí)例屬性與 [[Enumerable]] 標(biāo)記為 false 的某個(gè)原型屬性同名瓷产,那么該實(shí)例屬性將不會(huì)出現(xiàn)在 for-in 循環(huán)當(dāng)中矗蕊。
var hasDontEnumQuirk = function() { var o = {toString: function() { for(var prop in o) { if(prop == 'toString') { return false; } } } return true; }();
Example: Safari3- 中,會(huì)枚舉被隱藏的屬性。
var hasEnumShadowsQuirk() {
var o = {toString: function() {
var count = 0;
for (var prop in o) {
if (prop == 'toString') {
count++;
}
}
return (count > 1);
}();
3. Example: Safari3- 中神得,會(huì)枚舉被隱藏的屬性。
```
var hasEnumShadowsQuirk() {
var o = {toString: function() {
var count = 0;
for (var prop in o) {
if (prop == 'toString') {
count++;
}
}
return (count > 1);
}();
```
用戶代理檢測
-
用戶代理檢測通過檢測用戶代理字符串來確定實(shí)際使用的瀏覽器败富。在每一次 HTTP 請求過程中舟山,用戶代理字符串是作為相應(yīng)首部發(fā)送的,而且該字符串可以通過 JavaScript 的 navigator.userAgent 屬性訪問谎碍。
- 在服務(wù)器端鳞滨,用戶代理檢測被廣泛使用。
- 在客戶端蟆淀,用戶代理檢測的優(yōu)先級(jí)排在能力檢測 / 怪癖檢測之后拯啦。
電子欺騙:瀏覽器通過在自己的用戶代理字符串加入一些錯(cuò)誤或誤導(dǎo)信息,來達(dá)到欺騙服務(wù)器的目的熔任。
-
用戶代理字符串
- RFC2616 規(guī)定格式:標(biāo)識(shí)符/產(chǎn)品版本號(hào)
- IE4 ~ IE7:Mozilla/4.0 (平臺(tái); MSIE 版本號(hào); 操作系統(tǒng))
- IE8:Mozilla/4.0 (compatible; MSIE 版本號(hào); Trident/Trident 版本號(hào))
- Trident 記號(hào)是為了讓開發(fā)人員知道 IE8 是不是在兼容模式下運(yùn)行褒链,如果是,則 MSIE 版本號(hào)會(huì)變成 7疑苔。
- IE9:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
- IE9 兼容模式:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0)
- Gecko:Mozilla/Mozilla 版本號(hào) (平臺(tái); 加密類型; 操作系統(tǒng)或CPU; 語言; 預(yù)先發(fā)型版本) Gecko/Gecko 版本號(hào) 應(yīng)用程序或產(chǎn)品/引用程序或產(chǎn)品版本號(hào)
- 具體查手冊
- Firefox4: Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox 4.0.1
- Webkit:Mozilla/5.0 (平臺(tái); 加密類型; 操作系統(tǒng)或CPU; 語言) AppleWebkit/AppleWebkit 版本號(hào) (KHTML, like Gecko) Safari/Safari 版本號(hào)
- 基于 Webkit 的所有瀏覽器都將自己標(biāo)識(shí)為 Mozilla 5.0
- Safari3:Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebkit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5
- Konqueror 略
- Chrome7:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebkit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7
- Opera8:Opera/版本號(hào) (操作系統(tǒng)或CPU; 加密類型; 語言)
- Opera9:Mozilla/5.0 (Windows NT 5.1; U; en-US; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50
- Opera隱藏了自己的身份甫匹,偽裝成 Firefox 或 IE,無法將 Opera 與其他瀏覽器區(qū)別開來惦费。
- Opera10:Opera/9.80 (操作系統(tǒng)或CPU; 加密類型; 語言) Presto/Presto 版本號(hào) Version/版本號(hào)
- iOS 和 Android:同桌面版
-
用戶代理字符串檢測技術(shù)
-
識(shí)別呈現(xiàn)引擎
- 檢測五大引擎:IE兵迅、Gecko、WebKit薪贫、KHTML恍箭、Opera
- 使用模塊增強(qiáng)模式封裝檢測腳本
var client = function() { var engine = { ie: 0, gecko: 0, webkit: 0, khtml: 0, opera: 0, ver: null }; return { engine: engine }; }();
- 如果檢測到了哪個(gè)呈現(xiàn)引擎,就以浮點(diǎn)數(shù)值形式將該引擎的版本號(hào)寫入相應(yīng)的屬性瞧省。而呈現(xiàn)引擎的完整版本(是一個(gè)字符串)扯夭,則被寫入 ver 屬性。
if(client.engine.ie) { // 針對 IE 的代碼 } else if (client.engine.gecko > 1.5) { if (client.engine.ver == '1.8.1') { // 針對這個(gè)版本執(zhí)行某些代碼 } }
- 識(shí)別 Opera 必須檢測 window.opera 對象鞍匾。
if (window.opera) { engine.var = window.opera.version(); engine.opera = parseFloat(engine.ver);
- 識(shí)別 WebKit 和 KHTML 見手冊勉抓。
識(shí)別瀏覽器(具體見文檔)
var browser = { ie: 0, firefox: 0, safari: 0, konq: 0, opera: 0, chrome: 0, ver: null };
- 識(shí)別平臺(tái)(具體見文檔)
var system = { win: false, mac: false, x11: false }; var p = navigator.platform; system.win = p.indexOf('Win') == 0; system.mac = p.indexOf('Mac') == 0; system.x11 = (p.indexOf('X11') == 0) || (p.indexOf('Linux') == 0);
- 識(shí)別移動(dòng)設(shè)備(具體見文檔)
var system = { win: false, mac: false, x11: false, iphone: false, ipod: false, ipad: false, ios: false, android: false, nokiaN: false, winMobile: false }; var p = navigator.platform; system.win = p.indexOf('Win') == 0; system.mac = p.indexOf('Mac') == 0; system.x11 = (p.indexOf('X11') == 0) || (p.indexOf('Linux') == 0); system.iphone = ua.indexOf('iPhone') > -1; system.ipod = ua.indexOf('iPod') > -1; system.ipad = ua.indexOf('iPad') > -1; system.nokiaN = ua.indexOf('NokiaN') > -1; system.winMobile = (system.win == 'CE');
- 識(shí)別游戲系統(tǒng)(略)
-
完整的代碼(看文檔)