多浏览器研究

跨浏览器开发总结

1. 背景

CSS有很多东西,FFIE显示不一样的根本原因在于它们的默认显示不一样,而对于JS而言,则是因为IE要比Firefox宽松的多,比如我们定义个某个elementname而不定义其id,在IE中仍然可以使用getElementById找到这个元素。

涉及浏览器兼容的知识非常多,主要分为CSS hackJS兼容两个方面,如果碰到IEFF需要不同的写法的时候,我们尽量用jQuery去实现,因为jQuery特点就是兼容多浏览器,下面就此次Coding中碰到的一些常见的兼容问题做下总结。(其他item中,涉及到修改的地方建议用jQuery实现)。

此次调整遵循的原则是代码是所有前端页面(Aspxascxhtmhtml等文件)要显式声明DTD,用HTML 4.01 TraditionalXHTML 1.0 Traditional,所有前端页面要根据DTD声明通过VS2008自带的html4.01验证或xhtml 1.0 traditional验证,所有CSS文件要通过VS2008自带的CSS2.1验证,即在VS2008的编辑器中不能出现带有下划线提示不支持的状况,尽可能的找符合规范的写法替换提示不支持的写法,比如有时在tr中定义height=”10px”,这时编辑器提示不支持height属性,我们就应该修改为style=”height:10px”。

2. 开发测试指南

对于CSS:能采用W3C标准实现的,采用W3C方式;不能采用W3C标准实现的,使用CSS Hack方式;

对于JS:在出问题的代码段,尽可能用jQuery实现替代,不能实现之处做by 浏览器的定制代码。 

针对各个目标浏览器,使用浏览器相关的辅助Script调试工具,比如一些无法在Firefox中运行的脚本,通过Firebug监控时,Firebug会给予明确的提示。1IE 使用Developer Toolbar   2Firefox 使用Firebug

对于新开发页面的额外要求:

1、所有的标记都必须要有一个相应的结束标记。

2、所有标签的元素和属性的名字都必须使用小写。

3、所有的标记都必须合理嵌套。

4、所有的属性必须用引号""括起来。

5、给所有属性赋一个值,没有值的就重复本身。例如:checked="checked"

6、不要在注释内容中使用“--”,用等号或者空格替换内部的虚线。

7、在Header区的设置中加入版权的信息,格式如:<meta name="Copyright" content="明基逐鹿版权所有" />

3. 代码调试步骤

FlowER Web兼容多浏览器为例:进入需要做多浏览器兼容的页面后,首先可以使用vs2008编辑器自带的格式化功能,快捷键为Ctrl+K+D,将代码格式化,根据FlowER Web中做兼容时常碰到的问题,将易于出现兼容问题的地方列于下面,供其他人做类似功能时参考。

A:首先利用关键字document.all查找代码,只要有这样的写法,FF下都不支持。

B:利用关键字查找JS代码中涉及到createElementinnerTextchildNodeSelectSingleNode、selectNodesouterHTML的地方,这些地方以前的写法未必都有错,只不过IEFF下写法不同,有可能导致不兼容,是重点排查对象。

C:利用关键字<script查找,之前写法IE写法中大都缺少type="text/javascript",而出现绿色下划线提示。

D:利用关键字nowrap查找,FF下提示某个属性必须有对应的值,修改为nowrap="nowrap"。

E:利用关键字 checkedselecteddisabled等查找,修改为对应的 属性=“属性”。

F:代码修改中出现下划线提示的地方,一般问题表现为属性重复,如两个runat=”server”,table或者tr中出现heightCSS1.0CSS2.1不兼容的写法,或者引用的css样式不存在。

G:查找对象object,数组等后面的括号是否为小括号,因FF只支持中括号,注意修改为[]

H:有些hidden类型的type,以前写的时候,只有name没有id,现在为了兼容,我们一般根据id取值,给没有id的对象赋上id的值。

I:获取鼠标event的地方需要注意,FF下需要在js方法中传入event,才能使用包括定位及取键值等操作。
J:其他有可能引起不兼容问题的地方。

4. 代码调试注意事项

1、浏览器类型判断

function get Browser () 
{     if(navigator.userAgent.indexOf("MSIE") > 0) { 
        return "MSIE";  //IE
} }
if(isFirefox = navigator.userAgent.indexOf("Firefox") > 0){ 
        return "Firefox"; //FF
}  } 

jquery也提供了判断浏览器类型的接口:
格式为$.browser.xx;如果$.browser.msie为true则为IE,我们可以使用$.browser.msie、$.browser.mozilla、$.browser. opera、$.browser. safa是否为true进行判断是否为某种浏览器

2document.all问题

以前的代码中用的最多的document.all在W3C中是不支持的,如果要得到页面所有的元素的话,我们用document.getElementsByTagName("*")替代就OK,document.all.tagsgetElementsByTagName替换就可以,getElemntById 符合W3C的标准,可以按id(非name得到某一元素,当然用jquery实现更好。

3document.createElement创建HTML标签

IEdocument.createElement构建新的HTML标签,支持三种方式:
1 document.createElement("<input type=text>") ;
2 document.createElement("<input>")
3 document.createElement("input")
增加一个子节点时的方法IE也支持三种:
1 node.insertBefore(Element)
2 node.insertAfter(Element)
3 node.appendChild(Element)
而创建新标签以及增加子节点时,在FF下仅支持第三种,为了兼容IEFF,我们检查以前IE的写法是否是用第三种方法,如果不是,须修改,

4document.formName.elements[elementName]

IE下,可以使用document.formName.item(itemName)document.formName.elements[elementName]

FireFox下,只能用document.formName.elements[elementName]

解决方法:统一使用document.formName.elements[elementName]

5Selectonchange事件

对于select下拉选框的onchange事件,IE在使用上下键或者鼠标中间键(滚轮)时会激发onchange事件,而firefox不能激发,需要配合回车,或者onblur事件,为了修补这两者间的 差别,可以对firefox做相应的bug hack
原来的代码:
<select id="test" οnchange="alert(‘test onchange event’)" >
兼容FF的代码为:
<select id="test" οnchange="alert(‘test onchange event’)" οnkeyup="this.blur();this.focus();">

6、获取集合类对象

IE下,可以使用()[]获取集合类对象;Firefox下,只能使用[]获取集合类对象。

解决方法:统一使用[]获取集合类对象。如frame(0)frame(“main”)IE下是正确的,但在FF下就不正确,我们统一使用frame[0]frame[“main”]来兼容。

7window.close()关闭窗口

window.close()IE下可以执行关闭,但在Firefox下不关闭,这个跟FF的配置有关系,默认的配置是不允许代码关闭窗口的,打开配置步骤如下:
1.在Firefox地址栏里输入 about:config
2. 在配置列表中找到dom.allow_scripts_to_close_windows
3. 点右键的选切换把上面的false修改为true即可
<a href="javascript:window.opener=null;window.open('','_self');window.close();">Close Me</a>

8、自定义属性问题

说明:IE下,可以使用获取常规属性的方法来获取自定义属性,也可以使用getAttribute()获取自定义属性;Firefox下,只能使用getAttribute()获取自定义属性。

解决方法:统一通过getAttribute()获取自定义属性。

9eval("idName")问题

说明:IE下,可以使用eval("idName")或getElementById("idName")来取得id为idName的HTML对象;Firefox下只能使用getElementById("idName")来取得id为idName的HTML对象。

解决方法:统一用getElementById("idName")来取得id为idName的HTML对象。

10、变量名与某HTML对象ID相同的问题

说明:IE下,HTML对象的ID可以作为document的下属对象变量名直接使用;Firefox下则不能。Firefox下,可以使用与HTML对象ID相同的变量名;IE下则不能。

解决方法:使用document.getElementById("idName")代替document.idName。最好不要取HTML对象ID相同的变量名,以减少错误;在声明变量时,一律加上var,以避免歧义。

11const问题

说明:Firefox下,可以使用const关键字或var关键字来定义常量;IE下,只能使用var关键字来定义常量。

解决方法:统一使用var关键字来定义常量。

12keydownkeypresskeyup

Js捕捉键盘事件用,要关注浏览器的三种按键事件类型,即keydownkeypresskeyup,它们分别对应onkeydownonkeypressonkeyup这三个事件句柄。一个典型的按键会产生所有这三种事件,依次是keydownkeypress,然后是按键释放时候的keyup。而对于功能按键,如F1-F12BackspaceEnterEscapePageUPPageDown和箭头方向等,就不会产生keypress事件,但是可以产生keydownkeyup事件。然而在FF中,功能按键是可以产生keypress事件的。例如 onkeypress按键捕捉在IEFF下不同,FF下用e.which代替IE下的e.keyCode,e为我们页面上传过来的event事件,当然这个事件我们需要用兼容的写法:e = events || window.event;
我们通过代码对比了解下区别:IE下原来的写法:
function KeyCheckInt() //防止输入除数字以外的其他字符
{
  var reTest = /^(48|49|50|51|52|53|54|55|56|57)?$/
  if (!(reTest.test(event.keyCode)))
   {event.keyCode=null;}
}
<input type="text" id="txt" maxlength="10" οnkeypress="javascript: KeyCheckInt ();">
FF下的兼容写法为:
function KeyCheckInt(events) {
if (events.charCode == 0) return;//FF下屏蔽退格、方向键等功能键,charCode在IE下为undefined非0
var reTest = /^(48|49|50|51|52|53|54|55|56|57)?$/;
e = events || window.event;
if (e.charCode == 0) return;
 var code = e.keyCode || e.which;
    if (!(reTest.test(code))) { if (window.event)  //IE
        {   event.returnValue = false;   } 
else //Firefox                           
        {   events.preventDefault();  }
}  }
<input type="text" id="txt" maxlength="10" οnkeypress="javascript: KeyCheckInt(event);">
附按键事件、浏览器及按键事件对象属性关系表:


13event事件

window.event只能在IE下运行,而不能在Firefox下运行,这是因为Firefoxevent只能在事件发生的现场使用,另外FFeventcharCodeIEevent使用keyCode来判断输入键的代码。
IE下原来的写法为:
function on_enter() 

{    

var currentKey = window.event.keyCode;

    if (currentKey == 13) 

{    …… }        

}
<table width="570" align="center" οnkeypress="javascript:on_enter()">

W3C下的写法应该为:
function on_enter(events) 

{    

var currentKey = events.charCode || events.keyCode;

    if (currentKey == 13) 

{    …… }        

}
<table width="570" align="center" οnkeypress="javascript:on_enter(event)">
FF下必须有调用方法的地方传过来event事件才能使用到event事件。

14event.x与event.y问题

说明:IE下,even对象有x,y属性,但是没有pageX,pageY属性;Firefox下,even对象有pageX,pageY属性,但是没有x,y属性。

解决方法:使用mX(mX = event.x ? event.x : event.pageX)来代替IE下的event.x或者Firefox下的event.pageX。

15event.srcElement问题

说明:IE下,event对象有srcElement属性,但是没有target属性;Firefox下,even对象有target属性,但是没有srcElement属性。

解决方法:使用obj(obj = event.srcElement ? event.srcElement : event.target)来代替IE下的event.srcElement或者Firefox下的event.target。

16window.location.href问题

说明:IE或者Firefox2.0.x下,可以使用window.location或window.location.href;Firefox1.5.x下,只能使用window.location。

解决方法:使用window.location来代替window.location.href。

17body载入顺序

Firefoxbodybody标签没有被浏览器完全读入之前就存在;而IEbody则必须在body标签被浏览器完全读入之后才存在。
IE下此写法能起效而FF下不能:
<script type="text/javascript">
document.body.onclick = function(evt){
evt = evt || window.event;
alert(evt)
}
</script>
<body>
</body>
W3C兼容性的写法:
<body>
</body>
<script type="text/javascript">
document.body.onclick = function(evt){
evt = evt || window.event;
alert(evt);
} </script> 所以有时候在FF下,得不到我们想要的值,或者不能给对应的对象赋属性值的时候,就可能是由于FF加载顺序与IE的不同而造成的。

18parentNode

关于对象的父节点的问题IE中用obj.parentElementobj.parentNode得到父节点,使用parent.children得到结点的所有孩子结点,而FF中只支持obj.parentNodeparent.childNodes,所以解决方法使用obj.parentNode

19childNodes

IEFirefox中对childNodes的解释不同,IE不会包含空白文本结点,而Firefox会包含,所以会造成解析的节点FFIE下不同,在IE下能找到的节点,在FF下可能就找不到。
简单的一段节点代码:
<table><tr><td><input></input><input></input></td></tr></table>
可以有两种解决方法,第一种即改变找节点的方式,直接按生成的HTML标签进行查找getElementsByTagName(“input”)[0],第二种方法为Firefox在遍历子节点时,在for循环里不妨加上:if(childNode.nodeName=="#text") continue; 或者nodeType == 1;或者nodeType!=3;1代表元素element3代表文本text)或者加上一个判断document.getElementById(遍历节点的id)!=null。

function getRealChild(elem)

{  

var realChild = [];  

    for(var i = 0;i < elem.childNodes.length; i++)

    {  

     if(elem.childNodes[i].nodeType == 1)

        {  

         realChild.push(elem.childNodes[i]);

        }  

    }

        return realChild;  

 }

20innerText

innerText在IE中能正常工作,但是innerText在FireFox中却不行。需用textContent。

解决方法:

if(navigator.appName.indexOf("Explorer") > -1){ 

document.getElementById('element').innerText = "my text";

} else{ 

document.getElementById('element').textContent = "my text";

21、添加移除事件方法

其实针对对象添加事件,移除事件的兼容代码也可以这样写,
var obj=document.getElementById(“test”);
添加事件: if(obj.attachEvent) {
obj.attachEvent("onchange",function() {//for IE  
otherfunction(params);//这里可以给其实方法传参,也可以直接调用其它方法   });}
else if(obj.addEventListener) {//for W3c  
obj.addEventListener("change",function() { otherfunction(params);},false);   }
移除事件:obj.οnclick=null; 或者
if(obj.detachEvent) {
obj.detachEvent("onchange",test);   
} else if(obj.removeEventListener) {
obj.removeEventListener("change",test,false);}

22window.createPopup

FF模拟creptePopup代码如下:

if (!window.createPopup) {

  var __createPopup = function() {

  var SetElementStyles = function(element, styleDict) {

    var style = element.style;

    for (var styleName in styleDict) style[styleName] = styleDict[styleName];

  }

    var eDiv = document.createElement('div');

    SetElementStyles(eDiv, { 'position': 'absolute', 'top': 0 + 'px', 'left': 0 + 'px', 'width': 0 + 'px', 'height': 0 + 'px', 

'zIndex': 1000, 'display': 'none', 'overflow': 'hidden', 'border': '1px solid' });

    eDiv.body = eDiv;

    var opened = false;

    var setOpened = function(b) {

    opened = b;

     }

    var getOpened = function() {

    return opened;

    }

    var getCoordinates = function(oElement) {

    var coordinates = { x: 0, y: 0 };

    while (oElement) {

    coordinates.x += oElement.offsetTop;

    coordinates.y += oElement.offsetLeft;

    oElement = oElement.offsetParent;

    }

    return coordinates;

    }

     return { htmlTxt: '', document: eDiv, isOpen: getOpened(), isShow: false, hide: function() {

        SetElementStyles(eDiv, { 'top': 0 + 'px', 'left': 0 + 'px', 'width': 0 + 'px', 'height': 0 + 'px', 'display': 'none', 

'border': '1px solid' });

         eDiv.innerHTML = ''; this.isShow = false; }, show: function(iX, iY, iWidth, iHeight, oElement) {

     if (!getOpened()) { document.body.appendChild(eDiv); setOpened(true); };

     this.htmlTxt = eDiv.innerHTML;

     if (this.isShow) { this.hide(); };

     eDiv.innerHTML = this.htmlTxt; 

     var coordinates = getCoordinates(oElement);

     eDiv.style.left = (iX + coordinates.x) + 'px';

     eDiv.style.top = (iY + coordinates.y) + 'px';

      eDiv.style.width = iWidth + 'px'; 

      eDiv.style.height = iHeight + 'px'; 

      eDiv.style.display = 'block'; this.isShow = true; } }

    }

    window.createPopup = function() {

      return __createPopup();

     }

}

23CSS文件格式问题

关于CSS样式,需要注意css样式文件的保存格式须与引用样式文件页面的格式保持一致,否则的话,页面引用不到样式文件,例如在FlowER Web中,页面的文件格式是utf-8,而css文件的格式是gb2312,在FF下,就发生了页面文件没有样式的问题,将css文件格式另存为utf-8时,样式被页面成功的加载,另外 firefox不支持鼠标手型的属性值hand,只支持pointer,所以样式定义时用pointer代替hand

24、对象属性(长宽)赋值

FireFox中类似 obj.style.height = imgObj.height 的语句无效,解决方法:obj.style.height = imgObj.height + 'px';

25padding

网上很多资源都讲FF无法解释padding简写,我发现其实不然,至少FF3.6就可以支持。IE中当我们定义四个padding的时候,可以简写为padding: 1px 1px 1px 1px,正式的写法为: padding-top:1px; padding-right:1px; padding-bottom:1px; padding-left:1px; 
padding:1px; 代表上、右、下、左的padding各为1px
padding:1px 2px;代表上下1px,左右2px
padding:1px 2px 3px;代表上1px,左右2px,下3px

26、模态和非模态窗口问题 

说明:IE下,可以通过showModalDialogshowModelessDialog打开模态和非模态窗口;Firefox下则不能。

解决方法:直接使用window.open(pageURL,name,parameters)方式打开新窗口。

如果需要将子窗口中的参数传递回父窗口,可以在子窗口中使用window.opener来访问父窗口。例如:var parWin = window.openerparWin.document.getElementById("Aqing").value = "Aqing"

27frame问题 

以下面的frame为例:

<frame src="xxx.html" id="frameId" name="frameName" />

(1)访问frame对象:

IE:使用window.frameId或者window.frameName来访问这个frame对象。frameIdframeName可以同名。

Firefox:只能使用window.frameName来访问这个frame对象。

另外,在IEFirefox中都可以使用window.document.getElementById("frameId")来访问这个frame对象。

(2)切换frame内容:

IE Firefox中都可以使用window.document.getElementById("testFrame").src = "xxx.html"window.frameName.location="xxx.html"来切换frame的内容。

如果需要将frame中的参数传回父窗口(注意不是opener,而是parent frame),可以在frme中使用parent来访问父窗口。例如:parent.document.form1.filename.value="Aqing"

28ie,firefox以及其它浏览器对于 table 标签的操作都各不相同

ie中不允许对tabletrinnerHTML赋值,使用js增加一个tr时,使用appendChild方法也不管用。 

解决方法: 

//table追加一个空行: 

var row = otable.insertRow(-1); 

var cell = document.createElement("td"); 

cell.innerHTML = " "; 

cell.className = "XXXX"; 

row.appendChild(cell); 

29调用子框架或者其它框架中的元素的问题

IE中,可以用如下方法来取得子元素中的值

document.getElementById("frameName").(document.)elementName

window.frames["frameName"].elementName

在 FF中则需要改成如下形式来执行,与IE兼容:

window.frames["frameName"].contentWindow.document.elementName

window.frames["frameName"].document.elementName

30XMLHTT的区别

if (window.XMLHttpRequest) //mf

xmlhttp=new XMLHttpRequest() 

xmlhttp.open("GET",url,true) 

xmlhttp.send(null) 

//ie 

else if (window.ActiveXObject) // code for IE 

xmlhttp=new ActiveXObject("Microsoft.XMLHTTP") 

if (xmlhttp) 

{

xmlhttp.open("GET",url,true) 

xmlhttp.send() 

}

31nextSibling

function getNextSibling(obj)

{

if(obj.nextSibling.nodeType==3)

    {

     sibling=obj.nextSibling.nextSibling; // Moz. Opera

     }

     else

     {

      sibling=obj.nextSibling; // IE

     }

      return sibling;

}

32window.parent.location.reload()

FireFox中用window.parent.location = window.parent.location.href;

替换window.parent.location.reload()

 

5、其他参考资源

1CSS浏览器兼容问题整理
http://w3c.web600.net/html/XHTMLCSS/XHTMLCSSC/20090105/689.html

2JavaScript判断浏览器类型及版本
http://blog.csdn.net/nileel/archive/2009/04/17/4087159.aspx

3JavaScript动态插入技术
http://doc.itstrike.cn/Home/Article/JavaScript-dynamic-insertion

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值