跨瀏覽器開發
市場上的瀏覽器種類多的不計其數,它們的解釋引擎各不相同,期待所有瀏覽器都一致的支援JavaScript,CSS,DOM,那要等到不知什麼時候,然而開發者不能幹等著那天。歷史上已經有不少方法來解決瀏覽器相容問題了,主要分為兩種:1.userAgent字串檢測,2.物件檢測;當然,也不能考慮所有的瀏覽器,我們需要按照客戶需求來,如果可以確信瀏覽網站的使用者都使用或大部分使用IE瀏覽器,那麼你大可放心的使用IE專有的那些豐富的擴充套件,當然,一旦使用者開始轉向另一個瀏覽,那麼痛苦的日子便開始了。下面是市場上的主流瀏覽器列表:
- Internet Explorer
- Mozilla Firefox
- Google Chrome
- Opera
- Safari
注意,瀏覽器總是不斷更新,我們不但要為多種瀏覽器作相容處理,還要對同一瀏覽器多個版本作相容處理。比如IE瀏覽器,其6.0版本和7.0版本都很流行,因為微軟IE隨著作業系統繫結安裝(之前也是同步發行,微軟平均每兩年推出一款個人桌面,同樣IE也每兩年更新一次;直到現在,由於火狐的流行,IE工作組才加快IE的更新),所以更新的較慢,6.0版和7.0版有很大差別。
市場上還存在一些其它瀏覽器,但由於它們都是使用的上面所列瀏覽器的核心,或與上面瀏覽器使用了相同的解釋引擎,所以無需多作考慮。下面是主流的瀏覽器解釋引擎列表:
- Trident
Trident (又稱為MSHTML),是微軟的視窗作業系統(Windows)搭載的網頁瀏覽器—Internet Explorer的排版引擎的名稱,它的第一個版本隨著1997年10月Internet Explorer第四版釋出,之後不斷的加入新的技術並隨著新版本的Internet Explorer釋出。在未來最新的Internet Explorer第七版中,微軟將對Trident排版引擎做了的重大的變動,除了加入新的技術之外,並增加對網頁標準的支援。儘管這些變動已經在相當大的程度上落後了其它的排版引擎。使用該引擎的主要瀏覽器:IE,TheWorld,MiniIE,Maxthon,騰訊TT瀏覽器。事實上,這些瀏覽器是直接使用了IE核心,因為其userAgent字串中返回的資訊與IE是一模一樣的!
- Gecko
壁虎,英文為"Gecko"。Gecko是由Mozilla基金會開發的佈局引擎的名字。它原本叫作NGLayout。Gecko的作用是讀取諸如HTML、CSS、XUL和JavaScript等的網頁內容,並呈現到使用者螢幕或列印出來。Gecko已經被許多應用程式所使用,包括若干瀏覽器,例如Firefox、Mozilla Suite、Camino,Seamonkey等等
- Presto
Presto是一個由Opera Software開發的瀏覽器排版引擎,供Opera 7.0及以上使用。Presto取代了舊版Opera 4至6版本使用的Elektra排版引擎,包括加入動態功能,例如網頁或其部分可隨著DOM及Script語法的事件而重新排版。Presto在推出後不斷有更新版本推出,使不少錯誤得以修正,以及閱讀Javascript效能得以最佳化,併成為速度最快的引擎。
- KHTML
是HTML網頁排版引擎之一,由KDE所開發。KDE系統自KDE2版起,在檔案及網頁瀏覽器使用了KHTML引擎。該引擎以C++程式語言所寫,並以LGPL授權,支援大多數網頁瀏覽標準。由於微軟的Internet Explorer的佔有率相當高,不少以FrontPage製作的網頁均包含只有IE才能讀取的非標準語法,為了使KHTML引擎可呈現的網頁達到最多,部分IE專屬的語法也一併支援。目前使用KHTML的瀏覽器有Safari和Google Chrome。而KHTML也產生了許多衍生品,如:WebKit,WebCore引擎
利用userAgent檢測
下面是各大瀏覽器使用彈窗顯示的userAgent字串
IE瀏覽器:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)
火狐瀏覽器:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4
Opera瀏覽器:Opera/9.64 (Windows NT 5.1; U; Edition IBIS; zh-cn) Presto/2.1.1
Safari瀏覽器:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16
Google Chrome瀏覽器:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.33 Safari/530.5
可以使用下面的程式碼進行瀏覽器檢測
var Browser = { isIE:navigator.userAgent.indexOf("MSIE")!=-1, isFF:navigator.userAgent.indexOf("Firefox")!=-1, isOpera:navigator.userAgent.indexOf("Opera")!=-1, isSafari:navigator.userAgent.indexOf("Safari")!=-1 };
但這樣做並不是萬無一失的,一個特例便是Opera可以使用userAgent偽裝自己。下面是偽裝成IE的userAgent:Mozilla/5.0 (Windows NT 5.1; U; Edition IBIS; zh-cn; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.64;在完全偽裝的情況下,最後的“Opera 9.64”這個字串也不會出現,但Opera也有特殊的識別自身的方法,它會自動宣告一個opera全域性變數!
不但如此,我們的檢測還忽略了一點,就是那些使用相同引擎而品牌不同的瀏覽器,所以,直接檢測瀏覽器是沒有必要的,檢測瀏覽器的解釋引擎才是有必要的!
var Browser = { isIE:navigator.userAgent.indexOf("MSIE")>-1 && !window.opera, isGecko:navigator.userAgent.indexOf("Gecko")>-1 && !window.opera && navigator.userAgent.indexOf("KHTML") ==-1, isKHTML:navigator.userAgent.indexOf("KHTML")>-1, isOpera:navigator.userAgent.indexOf("Opera")>-1 };
物件檢測
瀏覽器檢測就到此結束了,下面應該講一下物件檢測!物件檢測其實是比瀏覽器檢測更加有效更加科學方法,而且我們之前一直在使用!
function addEvent(obj,evtype,bubbles) { if (obj.addEventListener) {....} else if (obj.attachEventListener) {....} }
物件檢測避免了瀏覽器引擎的多樣性,即當我們需要某種功能時,我們直接檢測瀏覽器是否支援該功能,而不用管瀏覽器是什麼牌子的!
什麼時候該使用瀏覽器檢測?什麼時候該使用物件檢測?
答案是能使用物件檢測時總該使用物件檢測,只有當必須對瀏覽器進行識別或無法使用物件檢測時才進行userAgent判斷
//一段用於將當前頁面新增到使用者收藏夾的程式碼,兩個不同的版本 window.external.addFavorite(location,"收藏頁面");//IE window.sidebar.addPanel("收藏頁面",location,"");//火狐 //由於在火狐下window也具有external屬性,並且在IE下判斷window.external.addFavorite會出錯 if (window.external.addFavorite) {...}//程式碼在IE下會出錯 //可以使用瀏覽器檢測,避免意外 if (Browser.isIE) { window.external.addFavorite(location,"收藏頁面");//IE } else if (Browser.isGecko) { window.sidebar.addPanel("收藏頁面",location,"");//火狐及其它相同引擎的瀏覽器 }
當你的指令碼和其它指令碼一起工作時(尤其和那些有問題的指令碼),有時候需要同時使用物件檢測與瀏覽器檢測
//對於window.innerWidth這些屬性,可能有些指令碼會建立一個相容多個瀏覽器的同名屬性 if (isIE) {//它聰明的使用了瀏覽器檢測 window.onresize = function () { window.innerWidth =document.documentElement.clientWidth; window.innerHeight = document.documentElement.clientHeight; }; window.onresize(); } //然而我們的指令碼對其進行檢測時就麻煩了 if (window.innerWidth) {//僅當在FF下時(FF支援position:fixed)才這樣做 obj.style.position="fixed"; obj.style.right=window.innerWidth/2+"px"; obj.style.bottom=window.innerWidth/2+"px"; .... }
當然,使用瀏覽器檢測始終是困難重重的,而對於測試文件是否支援某種特性,使用物件檢測可能是最有效的,還有另一種方法可以直接測試瀏覽器是否支援某標準!
測試與DOM標準的一致性
document物件有個implementation屬性,該屬性只有一個方法hasFeature(),用來測試瀏覽器是否支援某個DOM標準!
//測試是否支援XML DOM 1.0 var supportXML = document.implementation.hasFeature("XML", "1.0");
特 徵 | 支援的版本 | 描 述 |
---|---|---|
Core | 1.0, 2.0, 3.0 | 基本的DOM,給予了用層次樹來表示文件的能力 |
XML | 1.0,2.0,3.0 | 核心的XML擴充套件,增加了對CDATA Section、處理指令和實體的支援 |
HTML | 1.0,2.0 | XML的HTML擴充套件,增加了對HTML特定元素和實體的支援 |
Views | 2.0 | 基於特定樣式完成對文件的格式化 |
StyleSheets | 2.0 | 為文件關聯樣式表 |
CSS | 2.0 | 支援級聯樣式表1(CSS Level 1) |
CSS2 | 2.0 | 支援級聯樣式表2(CSS Level 2) |
Events | 2.0 | 通用DOM事件 |
UIEvents | 2.0 | 使用者介面事件 |
MouseEvents | 2.0 | 由滑鼠引起的事件(點選、滑鼠經過,等等) |
MutationEvents | 2.0 | 當DOM樹發生改變時引發的事件 |
HTMLEvents | 2.0 | HTML 4.01的事件 |
Range | 2.0 | 操作DOM樹中某個特定範圍的物件和方法 |
Traversal | 2.0 | 遍歷DOM樹的方法 |
LS | 3.0 | 在檔案和DOM樹之間同步地載入和儲存 |
LS-Async | 3.0 | 在檔案和DOM樹之間非同步地載入和儲存 |
Validation | 3.0 | 用於修改DOM樹之後仍然保持其有效性的方法 |
儘管這個相當方便,但是,使用implementation.hasFeature()有其明顯的缺陷——決定DOM實現是否對DOM標準的不同的部分相一致的,正是去進行實現的人(或公司)。要讓這個方法對於任何值都返回true,那是很簡單的,但這並不一定表示這個DOM實現真的和所有的標準都一致了。目前為止,最精確的瀏覽器要數Mozilla,但它多少也有一些並不完全和DOM標準一致的地方,這個方法卻返回為true。
錯誤處理
無盡的DOM相容性問題,如果總是使用物件檢測,就會帶來無盡的if else之類的分支語句,而且檢測某些物件的某些屬性時還會引發錯誤(尤其是在IE下),因為一般的JavaScript物件都可以轉換成布林值,但瀏覽器內建的一些方法或物件並不是JavaScript建立了,它們不一定能夠轉換成布林值!所以,使用錯誤處理語句不但避免了分支判斷,而且可以很優雅的處理錯誤!
try {} catch(e) {} finally {}
function addFav(address,name) { try { window.sidebar.addPanel(name,address,"");//FF方法 //在try語句中,如果指令碼出錯,會自動跳轉到catch語句執行 } catch(e) {//這裡的e是必須的,是語法的一部分,它表示一個錯誤物件 window.external.addFavorite(address,name);//IE //如果在這裡還出現錯誤,瀏覽器就會將這個錯誤丟擲 } }
Error物件
JavaScript內建了一個Error建構函式,可以建立一個錯誤物件,並可以使用throw語句手動丟擲錯誤!
var err = new Error(); throw err; //在IE中,會在錯誤視窗中顯示“未指明的錯誤”,而FF中則是空字串 //丟擲錯誤後,指令碼便會停止往下執行 var message = "我丟擲的錯誤!";//錯誤描述 err = new Error(message); throw err;
錯誤物件的屬性:message屬性儲存與錯誤物件相關的描述文字,number物件儲存錯誤程式碼。(錯誤號是 32 位的值。高 16 位字是裝置程式碼,而低字是實際的錯誤程式碼,不過錯誤程式碼對我們來說是沒什麼意義的)
try { undefined();//當try語句中指令碼出現錯誤時,會自動丟擲一個錯誤物件 } catch(e) {//e是自動建立是區域性變數,是Error的例項 alert("錯誤資訊是:"+e.message+"\n"+"錯誤程式碼是:"+e.number); } finally { //finally語句不管出現不出現錯誤,都將執行,一般用於出錯時釋放物件 }
onerror事件處理
DOM還有個onerror事件處理,當頁面載入出錯,圖象載入出錯及頁面指令碼出錯時會執行註冊的事件處理函式,一個常用的方法便是,在事件處理函式中返回true可以阻止瀏覽器出現錯誤提示!
window.onerror = function () { alert("出錯時你會看到我的!"); return true; };
另外一個用法是,可以監測影象的載入,如果影象載入出錯,可以重新載入影象(重新設定src屬性),也可以利用這個方法來測試一下影象是否存在(比如檢測使用者輸入的遠端影象的URL是否有效)
儘管使用try {} catch(e) {}語句,及使用onerror事件處理可以處理指令碼執行時錯誤,但它們是不能處理語法錯誤的!另外,使用錯誤處理語句並不是為了隱匿錯誤,而只是為了不干擾指令碼的繼續執行!
記錄錯誤
處理了這麼多錯誤後,記錄錯誤這樣的事情其實已經做過很多次了,主要有下面幾種方法
- 使用alert語句,好處是可以讓指令碼暫停執行,壞處便是彈窗不那麼好控制
- 使用document.write方法,這種方法自然避免了彈窗沒法關閉的情況,但不好控制指令碼執行!另一點便是,使用document.write會清空當前頁面!
- 與document.write方法類似的是使用一個自建的控制檯(比如一個DIV),然後將錯誤資訊一條一條的新增進去,這種方法肯定比document.write好多了!
- 還有另一種方法便是使用瀏覽器內建的控制檯,如在JavaScript裡面可以呼叫Java控制檯並向其中寫入資訊(前提是安裝了JRE)
java.lang.System.out.println("錯誤資訊!");//使用Java控制檯 console.log("錯誤資訊!");//使用火狐的控制檯 opera.postError("錯誤資訊!");//使用Opera的控制檯
使用庫提高開發效率
庫,就是一些可以方便的應用到當前的開發體系中的程式碼資源,JavaScript庫又被稱之為JavaScript框架,它是由一些類和普通函式構成。使用庫,可以使開發者不必關心程式的實現細節而只專心於業務邏輯。有很多流行的庫,它們大都能夠跨瀏覽器工作,並且採用了物件導向的良好編碼方式。 事實上,庫就是將之前我們寫的那些可以跨瀏覽器執行的函式集中到一個JS檔案中,以便能在所有頁面都能夠重複利用這些程式碼!當然,它們不是簡簡單單的將函式放到一起!下面是一些流行的庫及其優缺點:
- Prototype
不要把它和JavaScript裡面的prototype相混淆,Prototype是一個JS框架(官方稱之為框架),可以說是最早也是最出名的JS框架了。 它提供了許多JS物件導向的擴充套件及DOM操作API,之前它一直由於缺乏API文件而備受詬病,但現在其文件已很充足。 它的優點顯而易見,它只提供一些核心的,底層的功能,所以程式碼精簡,體積較小,易學易用,但由於其只具有底層功能,往往需要協同其它UI庫來執行! 目前,基於Prototype的庫已經有很多,著名的有整合Prototype庫的RoR Ajax庫,以及為Prototype提供許多視覺特效的Scriptaculous庫。
- jQuery
jQuery是一款同Prototype一樣優秀js開發庫,特別是對css和XPath的支援,使我們寫js變得更加方便!如果你不是個js高手又想寫出優秀的js效果,jQuery可以幫你達到目的!並且簡潔的語法和高效率一直是jQuery追求的目標。(其官網標語:jQuery將改變您書寫JavaScript的方式!)。 連YAHOO-UI都重用了很多jQuery的函式。支援外掛是jQuery的另一大優點,可以無即的擴充套件其功能。其缺點便是內部結構複雜,程式碼較為晦澀,一般的新手根本無法看懂其原始碼。所以jQuery適合開發而不適合一個剛開始學習建立JS庫的新手研究其原始碼。
- EXT-JS
EXT-JS前身是YAHOO-UI,EXT-JS是具有CS風格的Web使用者介面元件 能實現複雜的Layout佈局,介面效果可以和BackBase(另一JS庫)媲美,而且使用純javascript程式碼開發。真正的可編輯的表格Edit Grid,支援XML和Json資料型別,直接可以遷入grid。許多元件實現了對資料來源的支援,例如動態的佈局,可編輯的表格控制元件,動態載入的Tree 控制元件、動態拖拽效果等等。1.0 beta版開始同Jquery合作,推出基於jQuery的Ext 1.0,提供了更多有趣的功能。 對於一個喜歡JAVA的開發者來說,EXT-JS類似於java的結構,清晰明瞭,另一特點其可以實現華麗的令人震撼的WEB應用程式。當然,缺點也很嚴重,由於很多HTML及CSS程式碼都是EXT-JS自已建立的,所以其介面構造十分複雜,沒有讓我們自己寫CSS的餘地。
- Dojo
Dojo是目前最為強大的工業級JS框架。它在自己的Wiki上給自己下了一個定義,dojo是一個用JavaScript編寫的開源的DHTML工具箱。dojo很想做一個“大一統”的 工具箱,不僅僅是瀏覽器層面的,野心還是很大的。Dojo包括ajax, browser, event, widget等跨瀏覽器API,包括了JS本身的語言擴充套件,以及各個方面的工具類庫,和比較完善的UI元件庫,也被廣泛 應用在很多專案中,他的UI元件的特點是通過給html標籤增加tag的方式進行擴充套件,而不是通過寫JS來生成,dojo的API模仿Java類庫的組織 方式。 用dojo寫Web OS可謂非常方便。dojo現在已經4.0了,dojo強大的地方在於介面和特效的封裝,可以讓開發者快速構建一些相容標準的介面。 Dojo幾乎集了成了上面的JS庫的優點,其功能非同一般的強大,已得到了IBM和SUN的支援,但是自然的,其體積也十分大,總共有200多KB,另外,其語法也不如jQuery靈活,對JavaScript語言的增強也不如Prototype。
- Scriptaculous
Scriptaculous是基於prototype.js框架的JS效果。包含了6個js檔案,不同的檔案對應不同的js效果,所以說,如果底層用 prototype的話,做js效果用Scriptaculous那是再合適不過的了。優點便是基於Prototype,可以說是Prototype的外掛,不同的效果用不同的JS檔案分開存放,當然,依賴於Prototype也是其缺點。
- moo.fx
moo.fx是一個超級輕量級的javascript特效庫(3k),能夠與prototype.js框架一起使用。它非常快、易於使用、跨瀏覽器、符合標準,提供控制和修改任何HTML元素的CSS屬性,包括顏色。它內建檢查器能夠防止使用者通過多次或瘋狂點選來破壞效果。moo.fx整體採用模組化設計,所以可以在它的基礎上開發你需要的任何特效。輕量是其最大的優點,當然其缺點便是輕量級的,但是輕量級的JS庫能有如此強大已經很不錯了。
名稱空間
在建立庫之前,第一個要考慮的問題便是如何防止變數重名的問題!解決方案便是使用名稱空間!名稱空間是JAVA等語言中的一個概念,JavaScript並不支援名稱空間,但由於JavaScript的靈活性,我們可以使用物件來模似名稱空間!
var NameSpace={}; NameSpace.fn1 =function () {}; NameSpace.fn2 =function () {};
現在,fn1與fn2兩個函式就同屬於NameSpace名稱空間中了,它們不會也全域性中的fn1或fn2衝突,當然,在使用的時候必需加上NameSpace字首。這樣使用名稱空間並不是最簡單的,因為我們不得不在任何地方呼叫這些函式的時候都加上NameSpace字首。下面是使用JavaScript閉包來解決的方案:
var NameSpace={}; (function () { function fn1() { } function fn2() { fn1();//在這裡呼叫fn1不需要加NameSpace字首 } NameSpace.fn1=fn1;//將其新增為全域性變數NameSpace的屬性,以便能在函式外訪問 NameSpace.fn2=fn2; })();