天天看點

使用者代理字元串檢測技術「1」

作者:飛朋張

考慮到曆史原因以及現代浏覽器中使用者代理字元串的使用方式,通過使用者代理字元串來檢測特定的浏覽器并不是一件輕松的事。是以,首先要确定的往往是你需要多麼具體的浏覽器資訊。一般情況下,知道呈現引擎和最低限度的版本就足以決定正确的操作方法了。例如,我們不推薦使用下列代碼:

if (isIE6 || isIE7) { //不推薦!!!

//代碼

}

這個例子是想要在浏覽器為IE6或IE7時執行相應代碼。這種代碼其實是很脆弱的,因為它要依據特定的版本來決定做什麼。如果是IE8怎麼辦呢?隻要IE有新版本出來,就必須更新這些代碼。不過,像下面這樣使用相對版本号則可以避免此問題:

if (ieVer >=6){

//代碼

}

這個例子首先檢測IE的版本号是否至少等于6,如果是則執行相應操作。這樣就可以確定相應的代碼将來照樣能夠起作用。我們下面的浏覽器檢測腳本就将本着這種思路來編寫。

1. 識别呈現引擎

如前所述,确切知道浏覽器的名字和版本号不如确切知道它使用的是什麼呈現引擎。如果Firefox、Camino和Netscape都使用相同版本的Gecko,那它們一定支援相同的特性。類似地,不管是什麼浏覽器,隻要它跟Safari 3使用的是同一個版本的WebKit,那麼該浏覽器也就跟Safari 3具備同樣的功能。是以,我們要編寫的腳本将主要檢測五大呈現引擎:IE、Gecko、WebKit、KHTML和Opera。

為了不在全局作用域中添加多餘的變量,我們将使用子產品增強模式來封裝檢測腳本。檢測腳本的基本代碼結構如下所示:

var client = function(){

var engine = {

//呈現引擎

ie: 0,

gecko: 0,

webkit: 0,

khtml: 0,

opera: 0,

//具體的版本号

ver: null

};

//在此檢測呈現引擎、平台和裝置

return {

engine : engine

};

}();

這裡聲明了一個名為client的全局變量,用于儲存相關資訊。匿名函數内部定義了一個局部變量engine,它是一個包含預設設定的對象字面量。在這個對象字面量中,每個呈現引擎都對應着一個屬性,屬性的值預設為0。如果檢測到了哪個呈現引擎,那麼就以浮點數值形式将該引擎的版本号寫入相應的屬性。而呈現引擎的完整版本(是一個字元串),則被寫入ver屬性。作這樣的區分可以支援像下面這樣編寫代碼:

if (client.engine.ie) { //如果是IE,client.ie的值應該大于0

//針對IE的代碼

} else if (client.engine.gecko > 1.5){

if (client.engine.ver == "1.8.1"){

//針對這個版本執行某些操作

}

}

在檢測到一個呈現引擎之後,其client.engine中對應的屬性将被設定為一個大于0的值,該值可以轉換成布爾值true。這樣,就可以在if語句中檢測相應的屬性,以确定目前使用的呈現引擎,連具體的版本号都不必考慮。鑒于每個屬性都包含一個浮點數值,是以有可能丢失某些版本資訊。例如,将字元串"1.8.1"傳入parseFloat()後會得到數值1.8。不過,在必要的時候可以檢測ver屬性,該屬性中會儲存完整的版本資訊。

要正确地識别呈現引擎,關鍵是檢測順序要正确。由于使用者代理字元串存在諸多不一緻的地方,如果檢測順序不對,很可能會導緻檢測結果不正确。為此,第一步就是識别Opera,因為它的使用者代理字元串有可能完全模仿其他浏覽器。我們不相信Opera,是因為(任何情況下)其使用者代理字元串(都)不會将自己辨別為Opera。

要識别Opera,必須得檢測window.opera對象。Opera 5及更高版本中都有這個對象,用以儲存與浏覽器相關的辨別資訊以及與浏覽器直接互動。在Opera 7.6及更高版本中,調用version()方法可以傳回一個表示浏覽器版本的字元串,而這也是确定Opera版本号的最佳方式。要檢測更早版本的Opera,可以直接檢查使用者代理字元串,因為那些版本還不支援隐瞞身份。不過,2007底Opera的最高版本已經是9.5了,是以不太可能有人還在使用7.6之前的版本。那麼,檢測呈現引擎代碼的第一步,就是編寫如下代碼:

if (window.opera){

engine.ver = window.opera.version();

engine.opera = parseFloat(engine.ver);

}

這裡,将版本的字元串表示儲存在了engine.ver中,将浮點數值表示的版本儲存在了engine.opera中。如果浏覽器是Opera,測試window.opera就會傳回true;否則,就要看看是其他的什麼浏覽器了。

應該放在第二位檢測的呈現引擎是WebKit。因為WebKit的使用者代理字元串中包含"Gecko"和"KHTML"這兩個子字元串,是以如果首先檢測它們,很可能會得出錯誤的結論。

不過,WebKit的使用者代理字元串中的"AppleWebKit"是獨一無二的,是以檢測這個字元串最合适。下面就是檢測該字元串的示例代碼:

var ua = navigator.userAgent;

if (window.opera){

engine.ver = window.opera.version();

engine.opera = parseFloat(engine.ver);

} else if (/AppleWebKit\/(\S+)/.test(ua)){engine.ver = RegExp["$1"];engine.webkit = parseFloat(engine.ver);}

代碼首先将使用者代理字元串儲存在變量ua中。然後通過正規表達式來測試其中是否包含字元串"AppleWebKit",并使用捕獲組來取得版本号。由于實際的版本号中可能會包含數字、小數點和字母,是以捕獲組中使用了表示非空格的特殊字元(\S)。使用者代理字元串中的版本号與下一部分的分隔符是一個空格,是以這個模式可以保證捕獲所有版本資訊。test()方法基于使用者代理字元串運作正規表達式。如果傳回true,就将捕獲的版本号儲存在engine.ver中,而将版本号的浮點表示儲存在engine.webkit中。WebKit版本與Safari版本的詳細對應情況如下表所示。

Safari版本号最低限度的WebKit版本号Safari版本号最低限度的WebKit版本号1.0至1.0.285.71.3312.11.0.385.8.21.3.1312.51.1至1.1.11001.3.2312.81.2.2125.22.04121.2.3125.42.0.1412.71.2.4125.5.52.0.2416.112.0.3417.93.0.4523.102.0.4418.83.1525

有時候,Safari版本并不會與WebKit版本嚴格地一一對應,也可能會存在某些小版本上的差異。這個表中隻是列出了最可能的WebKit版本,但不保證精确。

接下來要測試的呈現引擎是KHTML。同樣,KHTML的使用者代理字元串中也包含"Gecko",是以在排除KHTML之前,我們無法準确檢測基于Gecko的浏覽器。KHTML的版本号與WebKit的版本号在使用者代理字元串中的格式差不多,是以可以使用類似的正規表達式。此外,由于Konqueror 3.1及更早版本中不包含KHTML的版本,故而就要使用Konqueror的版本來代替。下面就是相應的檢測代碼。

var ua = navigator.userAgent;

if (window.opera){

engine.ver = window.opera.version();

engine.opera = parseFloat(engine.ver);

} else if (/AppleWebKit\/(\S+)/.test(ua)){

engine.ver = RegExp["$1"];

engine.webkit = parseFloat(engine.ver);

} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)){engine.ver = RegExp["$1"];engine.khtml = parseFloat(engine.ver);}

與前面一樣,由于KHTML的版本号與後繼的标記之間有一個空格,是以仍然要使用特殊的非空格字元來取得與版本有關的所有字元。然後,将字元串形式的版本資訊儲存在engine.ver中,将浮點數值形式的版本儲存在engin.khtml中。如果KHTML不在使用者代理字元串中,那麼就要比對Konqueror後跟一個斜杠,再後跟不包含分号的所有字元。

在排除了WebKit和KHTML之後,就可以準确地檢測Gecko了。但是,在使用者代理字元串中,Gecko的版本号不會出現在字元串"Gecko"的後面,而是會出現在字元串"rv:"的後面。這樣,我們就必須使用一個比前面複雜一些的正規表達式,如下所示。

var ua = navigator.userAgent;

if (window.opera){

engine.ver = window.opera.version();

engine.opera = parseFloat(engine.ver);

} else if (/AppleWebKit\/(\S+)/.test(ua)){

engine.ver = RegExp["$1"];

engine.webkit = parseFloat(engine.ver);

} else if (/KHTML\/(\S+)/.test(ua)) {

engine.ver = RegExp["$1"];

engine.khtml = parseFloat(engine.ver);

} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){engine.ver = RegExp["$1"];engine.gecko = parseFloat(engine.ver);}

Gecko的版本号位于字元串"rv:"與一個閉括号之間,是以為了提取出這個版本号,正規表達式要查找所有不是閉括号的字元,還要查找字元串"Gecko/"後跟8個數字。如果上述模式比對,就提取出版本号并将其儲存在相應的屬性中。Gecko版本号與Firefox版本号的對應關系如下表所示。

Firefox版本号最低限度的Gecko版本号Firefox版本号最低限度的Gecko版本号1.01.7.53.51.9.11.51.8.03.61.9.22.01.8.14.02.0.03.01.9.0

與Safari跟WebKit一樣,Firefox與Gecko的版本号也不一定嚴格對應。

最後一個要檢測的呈現引擎就是IE了。IE的版本号位于字元串"MSIE"的後面、一個分号的前面,是以相應的正規表達式非常簡單,如下所示:

var ua = navigator.userAgent;

if (window.opera){

engine.ver = window.opera.version();

engine.opera = parseFloat(engine.ver);

} else if (/AppleWebKit\/(\S+)/.test(ua)){

engine.ver = RegExp["$1"];

engine.webkit = parseFloat(engine.ver);

} else if (/KHTML\/(\S+)/.test(ua)) {

engine.ver = RegExp["$1"];

engine.khtml = parseFloat(engine.ver);

} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){

engine.ver = RegExp["$1"];

engine.gecko = parseFloat(engine.ver);

} else if (/MSIE ([^;]+)/.test(ua)){engine.ver = RegExp["$1"];engine.ie = parseFloat(engine.ver);}

以上呈現引擎檢測腳本的最後一部分,就是在正規表達式中使用取反的字元類來取得不是分号的所有字元。IE通常會保證以标準浮點數值形式給出其版本号,但有時候也不一定。是以,取反的字元類[^;]可以確定取得多個小數點以及任何可能的字元。

2. 識别浏覽器

大多數情況下,識别了浏覽器的呈現引擎就足以為我們采取正确的操作提供依據了。可是,隻有呈現引擎還不能說明存在所需的JavaScript功能。蘋果公司的Safari浏覽器和谷歌公司的Chrome浏覽器都使用WebKit作為呈現引擎,但它們的JavaScript引擎卻不一樣。在這兩款浏覽器中,client.webkit都會傳回非0值,但僅知道這一點恐怕還不夠。對于它們,有必要像下面這樣為client對象再添加一些新的屬性。

var client = function(){

var engine = {

//呈現引擎

ie: 0,

gecko: 0,

webkit: 0,

khtml: 0,

opera: 0,

//具體的版本

ver: null

};

var browser = {//浏覽器ie: 0,firefox: 0,safari: 0,konq: 0,opera: 0,chrome: 0,//具體的版本ver: null};

//在此檢測呈現引擎、平台和裝置

return {

engine: engine,

browser: browser

};

}();

代碼中又添加了私有變量browser,用于儲存每個主要浏覽器的屬性。與engine變量一樣,除了目前使用的浏覽器,其他屬性的值将保持為0;如果是目前使用的浏覽器,則這個屬性中儲存的是浮點數值形式的版本号。同樣,ver屬性中在必要時将會包含字元串形式的浏覽器完整版本号。由于大多數浏覽器與其呈現引擎密切相關,是以下面示例中檢測浏覽器的代碼與檢測呈現引擎的代碼是混合在一起的。

//檢測呈現引擎及浏覽器

var ua = navigator.userAgent;

if (window.opera){

engine.ver = browser.ver = window.opera.version();engine.opera = browser.opera = parseFloat(engine.ver);

} else if (/AppleWebKit\/(\S+)/.test(ua)){

engine.ver = RegExp["$1"];

engine.webkit = parseFloat(engine.ver);

//确定是Chrome還是Safariif (/Chrome\/(\S+)/.test(ua)){browser.ver =

RegExp["$1"];browser.chrome = parseFloat(browser.ver);} else if (/Version\/(\S+)/.test(ua)){browser.ver = RegExp["$1"];browser.safari = parseFloat(browser.ver);} else {//近似地确定版本号var safariVersion = 1;if (engine.webkit safariVersion = 1;} else if (engine.webkit safariVersion = 1.2;} else if (engine.webkit safariVersion = 1.3;} else {safariVersion = 2;}browser.safari = browser.ver = safariVersion;

}

} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)){

engine.ver = browser.ver = RegExp["$1"];engine.khtml = browser.konq = parseFloat(engine.ver);

} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){

engine.ver = RegExp["$1"];

engine.gecko = parseFloat(engine.ver);

//确定是不是Firefoxif (/Firefox\/(\S+)/.test(ua)){browser.ver = RegExp["$1"];browser.firefox = parseFloat(browser.ver);}

} else if (/MSIE ([^;]+)/.test(ua)){

engine.ver = browser.ver = RegExp["$1"];engine.ie = browser.ie = parseFloat(engine.ver);

}

對Opera和IE而言,browser對象中的值等于engine對象中的值。對Konqueror而言,browser.konq和browser.ver屬性分别等于engine.khtml和engine.ver屬性。

為了檢測Chrome和Safari,我們在檢測引擎的代碼中添加了if語句。提取Chrome的版本号時,需要查找字元串"Chrome/"并取得該字元串後面的數值。而提取Safari的版本号時,則需要查找字元串"Version/"并取得其後的數值。由于這種方式僅适用于Safari 3及更高版本,是以需要一些備用的代碼,将WebKit的版本号近似地映射為Safari的版本号(參見上一小節中的表格)。

在檢測Firefox的版本時,首先要找到字元串"Firefox/",然後提取出該字元串後面的數值(即版本号)。當然,隻有呈現引擎被判别為Gecko時才會這樣做。

有了上面這些代碼之後,我們就可以編寫下面的邏輯。

if (client.engine.webkit) { //if it’s WebKit

if (client.browser.chrome){

//執行針對Chrome的代碼

} else if (client.browser.safari){

//執行針對Safari的代碼

}

} else if (client.engine.gecko){

if (client.browser.firefox){

//執行針對Firefox的代碼

} else {

//執行針對其他Gecko浏覽器的代碼

}

}

想要了解更多Java基礎知識,可以點選評論區連結和小編一起學習java吧,此視訊教程為初學者而著,零基礎入門篇!給同學們帶來全新的Java300集課程啦!java零基礎小白自學Java必備優質教程_手把手圖解學習Java,讓學習成為一種享受_哔哩哔哩_bilibili

繼續閱讀