JAVASCRIPT兼容性问题

一、检测浏览器的名称
问题:
    
不同的浏览器对 JavaScript 的标准支持也有不同,有时希望脚本能够在不同的浏览器上都能运行良好,这时需要对浏览器进行检测,确定其名称,以针对不同的浏览器编写相应的脚本。
解决方案:
    
使用 navigator 对象的 appName 属性。
    
比如,要检测浏览器是否为 IE ,可以这么做:
     var  isIE = (navigator.appName == "Microsoft Internet Explorer");
    document.write("is IE?" + isIE);

    
对于 FireFox navigator 对象的 appName 属性值为 "Netscape" Opera9.02 appName 属性值为 "Opera"( 其更早版本可能不同 )

二、检测浏览器的版本号:
问题:
    
随着浏览器的版本的更迭,浏览器所支持的脚本特性也在变化,有时候就需要针对不同的版本编写相应的脚本,那么如何获得浏览器的版本号?
解决方案:
    
通过解析 navigator 对象的 userAgent 属性来获得浏览器的完整版本号。
    IE
将自己标识为 MSIE ,后面带一个空格,版本号以及分号。所以我们只要取空格和分号之间的部分即可。如 Windows XP SP2 所带的 IE userAgent 属性值为 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)" ,可以看到其版本为 6.0 。可以用如下的函数来获取 IE 浏览器的版本号:
     function  getIEVersonNumber()
    {
        
var  ua = navigator.userAgent;
        
var  msieOffset = ua.indexOf("MSIE ");
        
if (msieOffset < 0)
        {
            
return  0;
        }
        
return  parseFloat(ua.substring(msieOffset + 5, ua.indexOf(";", msieOffset)));
    }

假设我们要为 IE5 及以上版本编写脚本,可以这么写:
     var  isIE5Min = (getIEVersonNumber() >= 5);
    
if (isIE5Min)
    {
        
// perform statements for IE 5 or later
    }

    
对于 FireFox Opera 等浏览器,也可以用 navigator.userAgent 属性来获取其版本号,只不过其形式与 IE 有所不同,如 FireFox
    Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
    Opera
Opera/9.02 (Windows NT 5.1; U; en) 根据这些形式,我们不难获得其版本号。但这些浏览器的其它版本没有测试过,其具体值不明确,如果要使用这种方法检测,请自行验证。

    
下面讨论下,上面的那段为 IE5 及以上版本浏览器编写的脚本,使用这种写法要注意:要用 >= 而不是 == ,一般情况下,我们可以假定浏览器是向后兼容的,所以使用 == 显然不能适应新版本;另一方面,我们上面的假定也仅仅是假定,不能确保是这样,如果浏览器的某些对象或属性不能向后兼容,我们的代码也会产生问题,所以建议,少用浏览器版本的比较,更多情况下,应检测是要用的对象或属性是否得到支持。

三、检测客户端的操作系统类型
根据上面的讨论可以看到, navigator.userAgent 属性通常含有操作系统的基本信息,但很不幸,没有统一的规则去根据 userAgent 获取准确的操作系统信息,因为这些值与浏览器的种类、浏览器的版本甚至浏览器的 OEM 版本都有关系。
通常我们能做的是,检测一些更为通用的信息,比如操作系统是 Windows 还是 Mac ,而不是去看是 Windows 98 还是 Windows XP 。其规则是所有的 Windows 版本都会含有 "Win" ,所有的 Macintosh 版本都含有 "Mac" ,所有的 Unix 则含有 "X11" ,而在 Linux 下则同时包含 "X11" "Linux" 。如:
     var  isWin = (navigator.userAgent.indexOf("Win") != -1);
    
var  isMac = (navigator.userAgent.indexOf("Mac") != -1);
    
var  isUnix = (navigator.userAgent.indexOf("X11") != -1);

通常用在为不同的操作系统设置不同的字体或位置等样式。

四、检测浏览器对特定对象的支持
问题:
    
如果需要编写对多种浏览器或浏览器的多个版本都能适用的脚本,就要进行检测一下,浏览器是否支持某个对象。当然这种检测主要是针对那些潜在的不兼容对象的语句。
解决方案:
    
早期的浏览器对于 img 元素的支持差别很大,所以要在脚本中操作 img 元素,需要检测浏览器是否支持。这时我们不需要对所有可能的浏览器一一检测,只需在必要的地方用下面的方式检测:
     function  rollover(imgName, imgSrc)
    {
        
//  如果支持 images 对象
         if (document.images)
        {
            
// statements go here
        }
    }

    
这种方法能够生效是基于一个事实:如果 document.images 对象不存在,那么 if 求值的结果为 false

    
使用这种方法,使得对对象的检测变得简单易行,但是我们要注意,对于那些不支持该对象的浏览器要如何较好得处理。看下面的代码:
     function  getImgAreas()
    {
        
var  result = 0;
        
for ( var  i = 0; i < document.images.length; i++)
        {
            result += (document.images[i].width * document.images[i].height);
        }
        
return  result;
    }
   
    
function  reportImageArea()
    {
        document.form1.imgData.value = getImgAreas();
    }

    
这里没用对象支持的检测。如果浏览器支持 document.images ,这两个函数运行正常;否则就会抛出异常。下面是改进的脚本:
     function  getImgAreas()
    {
        
var  result;
        
//  检测浏览器是否支持对象
         if  (document.images)
        {
            result = 0;
            
for  ( var  i = 0; i < document.images.length; i++)
            {
                result += (document.images[i].width * document.images[i].height);
            }
        }
        
//  返回值为一个数字或 null
         return  result;
    }
    
function  reportImageArea()
    {
        
//  现在可以判断返回值
         var  imgArea = getImgAreas();
        
var  output;
        
if  (imgArea ==  null )
        {
            
//  对于不支持 images 对象的浏览器也要给出相应信息
            output = "Unknown";
        } 
else  {
            output = imgArea;
        }
        document.reportForm.imgData.value = output;
    }

这样,不管浏览器是否支持该对象,都能给用户比较合理的信息,而不会跳出突兀的错误信息。

五、检测浏览器对特定属性和方法的支持
问题:
    
检测一个对象是否含有某个特定的属性或方法。
解决方案:
    
大多数情况下,可以用类似于下面的代码来判断:
     if (objectTest && objectPropertyTest)
    {
        
// OK to work with property
    }

    
先检测对象是否存在,然后再检测对象的属性是否存在。如果对象确实不存在,该方法有效;如果属性存在,但其值为 null, 0, false if 语句求值的结果也将是 false !所以这种方法并不安全,最好的方法是这样:
     if  (objectReference &&  typeof (objectReference.propertyName) != "undefined")
    {
        
// OK to work with property
    }

    
对于方法的检测也可用类似的方法:
     function  myFunction()
    {
        
if  (document.getElementById)
        {
            
//  这里可以使用 getElementById 方法
        }
    }
 
 
 
2----------------------
1.document.form.item  问题  
(1)
现有问题:  
现有代码中存在许多  document.formName.item("itemName")  这样的语句,不能在  MF  下运行  
(2)
解决方法:  
改用  document.formName.elements["elementName"] 
(3)
其它  
参见  2 

2. 
集合类对象问题  
(1)
现有问题:  
现有代码中许多集合类对象取用时使用  () IE  能接受, MF  不能。  
(2)
解决方法:  
改用  []  作为下标运算。如: document.forms("formName")  改为  document.forms["formName"]  
又如: document.getElementsByName("inputName")(1)  改为 document.getElementsByName("inputName")[1] 
(3)
其它  

3. window.event 
(1)
现有问题:  
使用  window.event  无法在  MF  上运行  
(2)
解决方法:  
MF 
 event  只能在事件发生的现场使用,此问题暂无法解决。可以这样变通:  
原代码 ( 可在 IE 中运行 )  
<input type="button" name="someButton" value="
提交 " οnclick="javascript:gotoSubmit()"/> 
... 
<script language="javascript"> 
function gotoSubmit() { 
... 
alert(window.event); // use window.event 
... 

</script> 

新代码 ( 可在 IE MF 中运行 )  
<input type="button" name="someButton" value="
提交 " οnclick="javascript:gotoSubmit(event)"/> 
... 
<script language="javascript"> 
function gotoSubmit(evt) { 
evt = evt ? evt : (window.event ? window.event : null); 
... 
alert(evt); // use evt 
... 

</script> 
此外,如果新代码中第一行不改,与老代码一样的话 (  gotoSubmit  调用没有给参数 ) ,则仍然只能在 IE 中运行,但不会出错。所以,这种方案  tpl  部分仍与老代码兼容。  

<script language="javascript"> 
function run(evnt) { 
if(!document.all) { 
alert(evnt.target.tagName); 

else { 
alert("IE
下无法看到效果! "); 


</script> 

<body> 
<table width="200" border="1" οnclick="run(event);"> 
<tr> 
<td bgcolor="#9900CC" onClick="run(event);">1</td> 
<td>2</td> 
<td>3</td> 
</tr> 
<tr> 
<td>11</td> 
<td>22</td> 
<td>33</td> 
</tr> 
<tr οnmοuseοver="run();"> 
<td>111</td> 
<td>222</td> 
<td>333</td> 
</tr> 
</table> 
</body> 
4. HTML 
对象的  id  作为对象名的问题  
(1)
现有问题  
 IE  中, HTML  对象的  ID  可以作为  document  的下属对象变量名直接使用。在  MF  中不能。  
(2)
解决方法  
 getElementById("idName")  代替  idName  作为对象变量使用。  

5. 
idName 字符串取得对象的问题  
(1)
现有问题  
IE 中,利用  eval(idName)  可以取得  id   idName   HTML  对象,在 MF  中不能。  
(2)
解决方法  
 getElementById(idName)  代替  eval(idName)  

6. 
变量名与某  HTML  对象  id  相同的问题  
(1)
现有问题  
 MF  中,因为对象  id  不作为  HTML  对象的名称,所以可以使用与  HTML  对象  id  相同的变量名, IE 中不能。  
(2)
解决方法  
在声明变量时,一律加上  var  ,以避免歧义,这样在  IE  中亦可正常运行。  
此外,最好不要取与  HTML  对象  id  相同的变量名,以减少错误。  
(3)
其它  
参见 问题

7. event.x 
 event.y  问题  
(1)
现有问题  
IE  中, event  对象有  x, y  属性, MF 中没有。  
(2)
解决方法  
MF 中,与 event.x  等效的是  event.pageX 。但 event.pageX IE 中没有。  
故采用  event.clientX  代替  event.x 。在 IE  中也有这个变量。  
event.clientX 
 event.pageX  有微妙的差别(当整个页面有滚动条的时候),不过大多数时候是等效的。  

如果要完全一样,可以稍麻烦些:  
mX = event.x ? event.x : event.pageX; 
然后用  mX  代替  event.x 
(3)
其它  
event.layerX 
 IE   MF  中都有,具体意义有无差别尚未试验。  


8. 
关于 frame 
(1)
现有问题  
 IE 可以用 window.testFrame 取得该 frame mf 中不行  
(2)
解决方法  
frame 的使用方面 mf ie 的最主要的区别是:  
如果在 frame 标签中书写了以下属性:  
<frame src="xx.htm" id="frameId" name="frameName" /> 
那么 ie 可以通过 id 或者 name 访问这个 frame 对应的 window 对象  
mf 只可以通过 name 来访问这个 frame 对应的 window 对象  
例如如果上述 frame 标签写在最上层的 window 里面的 htm 里面,那么可以这样访问  
ie
 window.top.frameId 或者 window.top.frameName 来访问这个 window 对象  
mf
只能这样 window.top.frameName 来访问这个 window 对象  

另外,在 mf ie 中都可以使用 window.top.document.getElementById("frameId") 来访问 frame 标签  
并且可以通过 window.top.document.getElementById("testFrame").src = 'xx.htm' 来切换 frame 的内容  
也都可以通过 window.top.frameName.location = 'xx.htm' 来切换 frame 的内容  
关于 frame window 的描述可以参见 bbs ‘window frame’ 文章  
以及 /test/js/test_frame/ 目录下面的测试  
----adun 2004.12.09
修改  

9. 
mf 中,自己定义的属性必须 getAttribute() 取得  
10.
mf 中没有  parentElement parement.children  而用  
parentNode parentNode.childNodes 
childNodes
的下标的含义在 IE MF 中不同, MF 使用 DOM 规范, childNodes 中会插入空白文本节点。  
一般可以通过 node.getElementsByTagName() 来回避这个问题。  
html 中节点缺失时, IE MF parentNode 的解释不同,例如  
<form> 
<table> 
<input/> 
</table> 
</form> 
MF
input.parentNode 的值为 form,  IE input.parentNode 的值为空节点  

MF
中节点没有 removeNode 方法,必须使用如下方法  node.parentNode.removeChild(node) 

11.const 
问题  
(1)
现有问题
 IE  中不能使用  const  关键字。如  const constVar = 32;  IE 中这是语法错误。  
(2)
解决方法
不使用  const  ,以  var  代替。  

12. body 
对象  
MF
body body 标签没有被浏览器完全读入之前就存在,而 IE 则必须在 body 完全被读入之后才存在  

13. url encoding 
js 中如果书写 url 就直接写 & 不要写 & 例如 var url = 'xx.jsp?objectName=xx&objectEvent=xxx'; 
frm.action = url
那么很有可能 url 不会被正常显示以至于参数没有正确的传到服务器  
一般会服务器报错参数没有找到  
当然如果是在 tpl 中例外,因为 tpl 中符合 xml 规范,要求 & 书写为
一般 MF 无法识别 js 中的


14. nodeName 
 tagName  问题  
(1)
现有问题:  
MF 中,所有节点均有  nodeName  值,但  textNode  没有  tagName  值。在  IE  中, nodeName  的使用好象  
有问题(具体情况没有测试,但我的 IE 已经死了好几次)。  
(2)
解决方法:  
使用  tagName ,但应检测其是否为空。  

15. 
元素属性  
IE
 input.type 属性为只读,但是 MF 下可以修改  


16. document.getElementsByName() 
 document.all[name]  的问题  
(1)
现有问题:  
 IE  中, getElementsByName() document.all[name]  均不能用来取得  div  元素(是否还有其它不能取的元素还不知道)。
 
第一: onload

    
网页加载完执行的函数,这个代码是从十大常用 javascript 的函数里面摘取的,当然有其他的实现方法,但这个函数写的真的非常巧妙。从效率方面也是一个非常值得使用的函数!
以下就是具体代码:
//--------------------------------------------------------
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload 
= 'function') {
window.onload = func;
}
else {
window.onload = function() {
oldonload();
func();
}
}
}
//--------------------------------------------------------
    
我不想一句一句的分析这个 addLoadEvent ,那是高手们很难接受的!我们就来说说它的兼容性!请看函数中 window.onload ,作者为什么不用 document.body.onload 呢?那是因为在 Firefox[ 以下全部简称 ff] document.body.onload undefined( 未定义 ) ,把一个函数赋值给 undefined 既不会发生什么事情,也不算出错!这个是让人头痛!好的,知道兼容性的厉害了吧?那么,以后在编写代码时注意一下就好了!  
第二: body
    
这个 body 对象也是困扰我们的东西,叫它对象不知道对不对?我不是学计算机专业的,专业术语还真不大清楚!
以下就是具体代码:
//--------------------------------------------------------
function getPageScroll(){
var yScroll;
if (self.pageYOffset) {
yScroll = self.pageYOffset;
}
// Explorer 6 Strict
else if (document.documentElement && document.documentElement.scrollTop){
yScroll = document.documentElement.scrollTop;
} else if (document.body) {// all other Explorers
yScroll = document.body.scrollTop;
}
arrayPageScroll = new Array(''
yScroll)
return arrayPageScroll;
}
//--------------------------------------------------------
    
这个函数作用是:浏览器滚动条滚动的高度读取。这是从 lightbox 中摘录的,读读这段代码,看到三次判断:
1.if (self.pageYOffset)
ff 进行判断处理;
2.if (document.documentElement && document.documentElement.scrollTop)
是对网页标准 (xHTML 1.1 DTD) 的判断,这里要说明一下:在加入  xHTML 1.1 DTD  文件头时 document.body.scrollTop 之类的值往往是 0 ,而这个是很难被调试察觉的 ( 我曾因此困惑很久,这里吐血传授经验啦! )
3.
则是我们常用 document.body.scrollTop
    
从这三次判断,可以想象作者思维的严密了!当然这是程序员的共性!

第三: attachEvent/addEventListener
    
这里是比较直接的区别,可是太多的直接却造成了编程的困扰,这不禁使人想起:到底有多少这样的直接不同函数?他们的差别到底在哪里?毕竟大家的大脑空间有限,这么多怎么记?可是这些是必须记住的!没事,这篇文章里常见的 js 兼容性都提到了,可以作为 家居旅行,随身携带 的小手册!
以下就是具体代码:
//--------------------------------------------------------
_observeAndCache
 function(element  name  observer  useCapture) {
if (
this.observers) this.observers = [];
if (element.addEventListener) {
this.observers.push([element
 name  observer  useCapture]);
element.addEventListener(name
 observer  useCapture);
} else if (element.attachEvent) {
this.observers.push([element
 name  observer  useCapture]);
element.attachEvent('on' + name
 observer);
}
}
//--------------------------------------------------------
    
意思是给对象添加事件。这是 Sam Stephenson prototype 中类的一部分,举这段代码,不是让你去慢慢分析那个 prototype.js 文件,只是说明在 ie Opera 下就可以使用 obj.attachEvent() ,但在 ff 下却只能使用 obj.addEventListener()
类似区别的还有:
detachEvent/removeEventListener
parentElement/parentNode
insertAdjacentElement/appendChild
srcElement/target
onmousewheel/DOMMouseScroll
clientY/pageY

第四:对象引用
1.getElementById
请看以下代码:
<!-- 1 -->
<input id="t1"><input type="button"
value="click me" οnclick="alert(t1.value)">
<!-- 2 -->
<input id="t1"><input type="button"
value="click me" οnclick="alert(document.getElementById('t1').value)">
    
两个都是获取文本框的值,但后者的兼容性就比前者好!对于 IE 来说,一个 HTML  元素的 ID 可以直接在脚本中当作变量名来使用,而 ff 中不可以。
getElementById
这个函数是非常有用、通用的函数,所以在引用对象时我们要尽量使用它!
2.var
请看以下代码:
//--------------------------------------------------------
echo=function(str){
document.write(str);
}
//--------------------------------------------------------
    
这个函数在 ie 上运行正常, ff 下却报错了,而在 echo 前加上 var 就正常了,这个就是我们提到 var 的目的。
3.[]
    document.forms(”formName”) 
改为  document.forms[”formName”] 目的:现有代码中许多集合类对象取用时使用  () ie  能接受, ff  却不能。
4.frame
的引用
    ie
可以通过 id 或者 name 访问这个 frame 对应的 window 对象,而 mf 只可以通过 name 来访问这个 frame 对应的 window 对象。

第五:脚本执行
    
让我们分别做个试验,请出 ie ff 分别运行一下下面一段 js
//--------------------------------------------------------
o={
foo
 function(){
alert("fly");
}
};
with (o) {
bar();
function bar(){
alert("fly");
}
foo();
}
//--------------------------------------------------------
    IE
下,上面的代码成功输出 fly ff 报错: bar 未定义!
    
当然这是一个小小的试验,大家很明显的看出 ie ff 的支持执行的情况: ie 脚本预解释执行, ff 脚本顺序执行!这是 javascript 编写和设计时必须注意的东西!

第六: XMLHttpRequest 对象
请看以下代码
//--------------------------------------------------------
function createRequest(){
if(typeof XMLHttpRequest
="undefined")? {
return new XMLHttpRequest();
}else if(typeof ActiveXObject
="undefined"){
var xmlHttp_ver? = false;
var xmlHttp_vers = [
"MSXML2.XmlHttp.5.0"

"MSXML2.XmlHttp.4.0"

"MSXML2.XmlHttp.3.0"

"MSXML2.XmlHttp"

"Microsoft.XmlHttp"
];
if(
xmlHttp_ver){
for(var i=0;i<xmlHttp_vers.length;i++){
try{
new ActiveXObject(xmlHttp_vers[i]);
xmlHttp_ver = xmlHttp_vers[i];
break;
}catch(oError){;}
}
}
if(xmlHttp_ver){
return new ActiveXObject(xmlHttp_ver);
}else{
throw new Error("Could not create XML HTTP Request.");
}
}else{
throw new Error("Your browser doesn't support an XML HTTP Request.");
}
}
//--------------------------------------------------------
    
意思是:得到 XMLHttpRequest 对象,是喜悦村里的一个兄弟写的。在 ie 下,一句 new ActiveXObject("MSXML2.XMLHTTP") 就可以搞定的东西,但这里我们花了这么多行代码来解决兼容性问题,这个函数作者更从原理入手: xmlHttp_vers  应该从版本高的往版本低的写,这样建立对象的数据调用的是你机子上安装过的最高版本的 MSXML2.XmlHttp 。十分巧妙和有效地得到了对象!

    
好了,其实关于 javascript 兼容性的例子还有很多,我们无法罗列所有,这里做了个简单介绍,更多的只能在编程工程中去慢慢体会了!当然本文仅仅讨论了 js 的兼容性,同时 css 的兼容性问题也是不可忽视的!解决兼容性最好的方法就是封装!
 
3----浏览器兼容性
JavaScript编程的最大问题来自不同的浏览器对各种技术和标准的支持。构建一个运行在不同浏览器(如IE和火狐)是一个困难的任务。因此几种AJAX JavaScript框架或者生成基于服务端逻辑或标记库的JavaScript,或者提供符合跨浏览器AJAX开发的客户端JavaScript库。一些流行的框架包括:AJAX.Net, Backbase, Bitkraft, Django, DOJO, DWR, MochiKit, Prototype, Rico, Sajax, Sarissa, and Script.aculo.us.  

  这些框架给开发人员更多的空间使得他们不需要担心跨浏览器的问题。虽然这些框架提升了开发人员构建应用的能力,但由于厂商已经开发了更细节的用户界面的打包组件解决方案,因此在AJAX组件市场中需要考虑一些其他因素。例如提供通用用户界面的组件如组合框和数据栅格的几个厂商,都可以被用来在应用中创建良好的通过类似电子数据表方式来查看和编辑数据的体验。但这些组件不仅是封装了组件的用户界面而且包括与服务端数据的通讯方式,这些组件通常使用基于标记方式来实现如ASP.Net或JSF控件。
Ajax 在本质上是一个浏览器端的技术,首先面临无可避免的第一个问题即是浏览器的兼容性问题。各家浏览器对于 JavaScript/DOM/CSS 的支持总有部分不太相同或是有 Bug ,甚至同一浏览器的各个版本间对于 JavaScript/DOM/CSS 的支持也有可能部分不一样。这导致程序员在写 Ajax 应用时花大部分的时间在调试浏览器的兼容性而非在应用程序本身。因此,目前大部分的 Ajax 链接库或开发框架大多以 js 链接库的形式存在,以定义更高阶的 JavaScript API JavaScript 对象(模板)、或者 JavaScript Widgets 来解决此问题。如 prototype.js
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值