javascript快速入门21—DOM总结

跨浏览器开发

市场上的浏览器种类多的不计其数,它们的解释引擎各不相同,期待所有浏览器都一致的支持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)

javascript快速入门21—DOM总结  - 图1

火狐浏览器:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4

javascript快速入门21—DOM总结  - 图2

Opera浏览器:Opera/9.64 (Windows NT 5.1; U; Edition IBIS; zh-cn) Presto/2.1.1

javascript快速入门21—DOM总结  - 图3

Safari浏览器:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16

javascript快速入门21—DOM总结  - 图4

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

javascript快速入门21—DOM总结  - 图5

可以使用下面的代码进行浏览器检测

  1. varBrowser={
  2. isIE:navigator.userAgent.indexOf("MSIE")!=-1,
  3. isFF:navigator.userAgent.indexOf("Firefox")!=-1,
  4. isOpera:navigator.userAgent.indexOf("Opera")!=-1,
  5. 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全局变量!

不但如此,我们的检测还忽略了一点,就是那些使用相同引擎而品牌不同的浏览器,所以,直接检测浏览器是没有必要的,检测浏览器的解释引擎才是有必要的!

  1. varBrowser={
  2. isIE:navigator.userAgent.indexOf("MSIE")>-1&&!window.opera,
  3. isGecko:navigator.userAgent.indexOf("Gecko")>-1&&!window.opera && navigator.userAgent.indexOf("KHTML")==-1,
  4. isKHTML:navigator.userAgent.indexOf("KHTML")>-1,
  5. isOpera:navigator.userAgent.indexOf("Opera")>-1};

对象检测

浏览器检测就到此结束了,下面应该讲一下对象检测!对象检测其实是比浏览器检测更加有效更加科学方法,而且我们之前一直在使用!

  1. function addEvent(obj,evtype,bubbles){if(obj.addEventListener){....}elseif(obj.attachEventListener){....}
  2. }

对象检测避免了浏览器引擎的多样性,即当我们需要某种功能时,我们直接检测浏览器是否支持该功能,而不用管浏览器是什么牌子的!

什么时候该使用浏览器检测?什么时候该使用对象检测?

答案是能使用对象检测时总该使用对象检测,只有当必须对浏览器进行识别或无法使用对象检测时才进行userAgent判断

  1. //一段用于将当前页面添加到用户收藏夹的代码,两个不同的版本
  2. window.external.addFavorite(location,"收藏页面");//IE
  3. window.sidebar.addPanel("收藏页面",location,"");//火狐
  4. //由于在火狐下window也具有external属性,并且在IE下判断window.external.addFavorite会出错
  5. if(window.external.addFavorite){...}//代码在IE下会出错
  6. //可以使用浏览器检测,避免意外
  7. if(Browser.isIE){
  8. window.external.addFavorite(location,"收藏页面");//IE
  9. }elseif(Browser.isGecko){
  10. window.sidebar.addPanel("收藏页面",location,"");//火狐及其它相同引擎的浏览器
  11. }

当你的脚本和其它脚本一起工作时(尤其和那些有问题的脚本),有时候需要同时使用对象检测与浏览器检测

  1. //对于window.innerWidth这些属性,可能有些脚本会创建一个兼容多个浏览器的同名属性
  2. if(isIE){//它聪明的使用了浏览器检测
  3. window.onresize =function(){
  4. window.innerWidth =document.documentElement.clientWidth;
  5. window.innerHeight = document.documentElement.clientHeight;
  6. };
  7. window.onresize();
  8. }//然而我们的脚本对其进行检测时就麻烦了
  9. if(window.innerWidth){//仅当在FF下时(FF支持position:fixed)才这样做
  10. obj.style.position="fixed";
  11. obj.style.right=window.innerWidth/2+"px";
  12. obj.style.bottom=window.innerWidth/2+"px";
  13. ....
  14. }

当然,使用浏览器检测始终是困难重重的,而对于测试文档是否支持某种特性,使用对象检测可能是最有效的,还有另一种方法可以直接测试浏览器是否支持某标准!

测试与DOM标准的一致性

document对象有个implementation属性,该属性只有一个方法hasFeature(),用来测试浏览器是否支持某个DOM标准!

  1. //测试是否支持XML DOM 1.0
  2. var supportXML = document.implementation.hasFeature("XML","1.0");
特 征支持的版本描 述
Core1.0, 2.0, 3.0基本的DOM,给予了用层次树来表示文档的能力
XML1.0,2.0,3.0核心的XML扩展,增加了对CDATA Section、处理指令和实体的支持
HTML1.0,2.0XML的HTML扩展,增加了对HTML特定元素和实体的支持
Views2.0基于特定样式完成对文档的格式化
StyleSheets2.0为文档关联样式表
CSS2.0支持级联样式表1(CSS Level 1)
CSS22.0支持级联样式表2(CSS Level 2)
Events2.0通用DOM事件
UIEvents2.0用户界面事件
MouseEvents2.0由鼠标引起的事件(点击、鼠标经过,等等)
MutationEvents2.0当DOM树发生改变时引发的事件
HTMLEvents2.0HTML 4.01的事件
Range2.0操作DOM树中某个特定范围的对象和方法
Traversal2.0遍历DOM树的方法
LS3.0在文件和DOM树之间同步地载入和存储
LS-Async3.0在文件和DOM树之间异步地载入和存储
Validation3.0用于修改DOM树之后仍然保持其有效性的方法

尽管这个相当方便,但是,使用implementation.hasFeature()有其明显的缺陷——决定DOM实现是否对DOM标准的不同的部分相一致的,正是去进行实现的人(或公司)。要让这个方法对于任何值都返回true,那是很简单的,但这并不一定表示这个DOM实现真的和所有的标准都一致了。目前为止,最精确的浏览器要数Mozilla,但它多少也有一些并不完全和DOM标准一致的地方,这个方法却返回为true。

错误处理

无尽的DOM兼容性问题,如果总是使用对象检测,就会带来无尽的if else之类的分支语句,而且检测某些对象的某些属性时还会引发错误(尤其是在IE下),因为一般的JavaScript对象都可以转换成布尔值,但浏览器内置的一些方法或对象并不是JavaScript创建了,它们不一定能够转换成布尔值!所以,使用错误处理语句不但避免了分支判断,而且可以很优雅的处理错误!

try {} catch(e) {} finally {}

  1. function addFav(address,name){try{
  2. window.sidebar.addPanel(name,address,"");//FF方法
  3. //在try语句中,如果脚本出错,会自动跳转到catch语句执行
  4. }catch(e){//这里的e是必须的,是语法的一部分,它表示一个错误对象
  5. window.external.addFavorite(address,name);//IE
  6. //如果在这里还出现错误,浏览器就会将这个错误抛出
  7. }
  8. }

Error对象

JavaScript内置了一个Error构造函数,可以创建一个错误对象,并可以使用throw语句手动抛出错误!

  1. var err =newError();throw err;//在IE中,会在错误窗口中显示“未指明的错误”,而FF中则是空字符串
  2. //抛出错误后,脚本便会停止往下执行
  3. var message ="我抛出的错误!";//错误描述
  4. err =newError(message);throw err;

错误对象的属性:message属性保存与错误对象相关的描述文字,number对象保存错误代码。(错误号是 32 位的值。高 16 位字是设备代码,而低字是实际的错误代码,不过错误代码对我们来说是没什么意义的)

  1. try{
  2. undefined();//当try语句中脚本出现错误时,会自动抛出一个错误对象
  3. }catch(e){//e是自动创建是局部变量,是Error的实例
  4. alert("错误信息是:"+e.message+"\n"+"错误代码是:"+e.number);
  5. }finally{//finally语句不管出现不出现错误,都将执行,一般用于出错时释放对象
  6. }

onerror事件处理

DOM还有个onerror事件处理,当页面载入出错,图象载入出错及页面脚本出错时会执行注册的事件处理函数,一个常用的方法便是,在事件处理函数中返回true可以阻止浏览器出现错误提示!

  1. window.onerror =function(){
  2. alert("出错时你会看到我的!");returntrue;
  3. };

另外一个用法是,可以监测图像的载入,如果图像载入出错,可以重新载入图像(重新设置src属性),也可以利用这个方法来测试一下图像是否存在(比如检测用户输入的远程图像的URL是否有效)

尽管使用try {} catch(e) {}语句,及使用onerror事件处理可以处理脚本运行时错误,但它们是不能处理语法错误的!另外,使用错误处理语句并不是为了隐匿错误,而只是为了不干扰脚本的继续执行!

记录错误

处理了这么多错误后,记录错误这样的事情其实已经做过很多次了,主要有下面几种方法

  • 使用alert语句,好处是可以让脚本暂停运行,坏处便是弹窗不那么好控制
  • 使用document.write方法,这种方法自然避免了弹窗没法关闭的情况,但不好控制脚本运行!另一点便是,使用document.write会清空当前页面!
  • 与document.write方法类似的是使用一个自建的控制台(比如一个DIV),然后将错误信息一条一条的添加进去,这种方法肯定比document.write好多了!
  • 还有另一种方法便是使用浏览器内置的控制台,如在JavaScript里面可以调用Java控制台并向其中写入信息(前提是安装了JRE)
    1. java.lang.System.out.println("错误信息!");//使用Java控制台
    2. console.log("错误信息!");//使用火狐的控制台
    3. 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的灵活性,我们可以使用对象来模似命名空间!

  1. varNameSpace={};
  2. NameSpace.fn1 =function(){};
  3. NameSpace.fn2 =function(){};

现在,fn1与fn2两个函数就同属于NameSpace命名空间中了,它们不会也全局中的fn1或fn2冲突,当然,在使用的时候必需加上NameSpace前缀。这样使用命名空间并不是最简单的,因为我们不得不在任何地方调用这些函数的时候都加上NameSpace前缀。下面是使用JavaScript闭包来解决的方案:

  1. varNameSpace={};
  2. (function(){function fn1(){
  3. }function fn2(){
  4. fn1();//在这里调用fn1不需要加NameSpace前缀
  5. }
  6. NameSpace.fn1=fn1;//将其添加为全局变量NameSpace的属性,以便能在函数外访问
  7. NameSpace.fn2=fn2;
  8. })();

原文: https://wizardforcel.gitbooks.io/liyanhui-tutorials/content/73.html