1. 兼容性问题的根本因素
浏览器最重要或者说核心的部分是“Rendering Engine”,可大概译为“渲染引擎”,所谓的浏览器内核也就是浏览器所采用的渲染引擎,渲染引擎决定了浏览器如何显示网页的内容以及页面的格式信息。不同的浏览器内核对网页编写语法的解释也有不同,因此同一网页在不同的内核的浏览器里的渲染(显示)效果也可能不同,这也是网页编写者需要在不同内核的浏览器中测试网页显示效果的原因。
前段时间进行了前端网页的bug修改,发现多数的bug均发生在IE8浏览器上,经过查证发现:IE8的JavaScript引擎是Jscript,IE9开始用Chakra,这两个版本区别很大,这就造成了IE8对于个别属性、方法的不支持或是显示效果上的差异,这里是我遇到的一些IE8上比较典型的兼容性问题及其解决办法。
2. indexOf
2.1 问题
前端代码中有多处地方使用javascript数组的indexOf方法,用于查询某数组中是否存在某元素,然而测试,在IE8不支持数组元素的indexOf()方法。如下
var location = ['1','2','3'];
var serverData = [‘1’ , ’4’, ‘5’];
for( var i = 0; i < serverData.length; i++ )
{
if( location.indexOf( serverData[i].location ) == -1 )
location.push( serverData[i]);
}
}
上述js代码的功能是将数组serverData出现的新元素添加如location中。该方法在火狐、chrome浏览器中支持,但在IE8中并不支持。
2.2 解决办法
方法一:
数组元素转化为字符串后,再使用indexOf()方法。
var location = ['1','2','3'];
var serverData = [‘1’ , ’4’, ‘5’];
for( var i = 0; i < serverData.length; i++ )
{
if( location.join().indexOf( serverData[i].location ) == -1 )
location.push( serverData[i]);
}
方法二(推荐方法):
添加如下js代码:
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
当浏览器对于数组不支持indexOf()方法时,就为数组添加该方法。
该方法会在浏览器不支持数组的indexOf方法时,为其手动添加indexOf方法,实现浏览器的兼容。
3. ajax请求响应的异常状态码1223
3.1 问题
1- AJAX状态值与状态码区别
AJAX状态值是指,运行AJAX所经历过的几种状态,无论访问是否成功都将响应的步骤,可以理解成为AJAX运行步骤。如:正在发送,正在响应等,由AJAX对象与服务器交互时所得;使用“ajax.readyState”获得。(由数字1~4单位数字组成)
AJAX状态码是指,无论AJAX访问是否成功,由HTTP协议根据所提交的信息,服务器所返回的HTTP头信息代码,该信息使用“ajax.status”所获得。如下判断:
if(ajax.readyState == 4 && ajax.status ajax.status == 200)
最近在处理前端与服务器的交互时,发现在IE下发出的ajax请求,有时会会返回错误的状态码 1223。我们常用的状态码列表中并没有该状态码。
3.2 解决办法
经过查证发现,在判定一个请求是否已经完成的时候,验证xhr的status有一点是需要注意的:“有的浏览器会错误地返回204状态码”,而IE(非原生的XHR对象)中会将204设置为1223,Opera会在取得204时将status设置为0,而Safari 3之前的版本会将status设置为undefined。
我们常用的状态码:
200——请求成功
204——请求收到,但返回信息为空
即在IE浏览器下,状态码1223与204是等价的,同样为请求收到,但返回信息为空,我们只需要将ajax.status==1223的判断加入ajax.status==204的判断中即可。
4. 文件下载
4.1 非IE浏览器的文件下载
4.1.1 方法
在前端的修改中,我遇到了两种下载文件的方式,一种是将服务器的文件下载到本地,另一种是在本地将数据保存为对应格式的文件下载下来。现在推荐的是第一种方法。
var aLink = document.createElement("a"),
evt = document.createEvent("HTMLEvents"),
isData = contentOrPath.slice(0, 5) === "data:",
isPath = contentOrPath.lastIndexOf(".") > -1;
// 初始化点击事件
evt.initEvent("click",false,false);
// 添加文件下载名
aLink.download = fileName;
// 如果是 path 或者 dataURL 直接赋值
// 如果是 file 或者其他内容,使用 Blob 转换
aLink.href = (isPath || isData) ? contentOrPath
: URL.createObjectURL(new Blob([contentOrPath]));
aLink.dispatchEvent(evt);
利用a标签可以实现下载文件的效果
<a href="/images/myw3schoolimage.jpg" download="w3logo">
通过为其添加download属性,可以使其完成文件下载的功能。
4.1.2 局限
只有 Firefox 和 Chrome 支持 download 属性。
IE下的文件下载可以通过document.execCommand()完成。
这里使用了事件模拟来触发标签a的下载事件。
4.1.3 非IE浏览器的模拟事件
模拟事件大致分为模拟鼠标事件、模拟键盘事件、模拟变动事件及模拟HTML事件等。
模拟事件的过程大致分为三步:
1. 创建event对象。
通过createEvent()可以创建event对象,而传入字符串的不同可以决定,如var event = document.createEvent(“MouseEvents”); //创建鼠标事件对象
event = document.createEvent(“KeyboardEvent”); //创建键盘事件对象
2. 初始化事件对象
不同类型的模拟事件对象有着不同的初始化参数
event.initMouseEvent(“click”, true, true, document.defaultView, 0, 0, 0, 0, 0,false, false, false, false, 0, null);// 鼠标事件对象初始化
iniMouseEvent()方法接受15参数,参数如下:
type string类型 :要触发的事件类型,例如‘click’。
bubbles Boolean类型:表示事件是否应该冒泡,针对鼠标事件模拟,该值应该被设置为true。
cancelable bool类型:表示该事件是否能够被取消,针对鼠标事件模拟,该值应该被设置为true。
view 抽象视图:事件授予的视图,这个值几乎全是document.defaultView.
detail int类型:附加的事件信息这个初始化时一般应该默认为0。
screenX int类型 : 事件距离屏幕左边的X坐标
screenY int类型 : 事件距离屏幕上边的y坐标
clientX int类型 : 事件距离可视区域左边的X坐标
clientY int类型 : 事件距离可视区域上边的y坐标
ctrlKey Boolean类型 : 代表ctrol键是否被按下,默认为false。
altKey Boolean类型 : 代表alt键是否被按下,默认为false。
shiftKey Boolean类型 : 代表shif键是否被按下,默认为false。
metaKey Boolean类型: 代表meta key 是否被按下,默认是false。
button int类型: 表示被按下的鼠标键,默认是零.
relatedTarget (object) : 事件的关联对象.只有在模拟mouseover 和 mouseout时用到。
event.initKeyboardEvent(“keydown”, true, true, document.defaultView, “a”,0, “Shift”, 0); //键盘事件对象初始化。
初始化键盘事件的参数有以下几个:
type (string) - 要触发的事件类型,例如“keydown”.
bubbles (Boolean) — 代表事件是否应该冒泡.
cancelable (Boolean) — 代表事件是否可以被取消.
view (AbstractView) — 被授予事件的是图. 通常值为:document.defaultView.
key (string) — 按下的键对应的code.
location (integer) — 按下键所在的位置. 0 :默认键盘, 1 左侧位置, 2 右侧位置, 3 数字键盘区, 4 虚拟键盘区, or 5 游戏手柄.
modifiers (string) — 一个有空格分开的修饰符列表.
repeat (integer) — 一行中某个键被按下的次数.
3. 触发模拟事件
通过dispatchEvent(event)来实现。
4.2 IE下的文件下载
4.2.1 方法
IE下的文件下载可以通过document.execCommand()完成。
4.2.2 execCommand()
document.execCommand()方法处理Html数据时常用语法格式如下:
document.execCommand(sCommand[,交互方式, 动态参数])
其 中:sCommand为指令参数(如下例中的”2D-Position”),交互方式参数如果是true的话将显示对话框,如果为false的话,则不显 示对话框(下例中的”false”即表示不显示对话框),动态参数一般为一可用值或属性值(如下例中的”true”)。
document.execCommand(”2D-Position”,”false”,”true”);
调用execCommand()可以实现浏览器菜单的很多功能. 如保存文件,打开新文件,撤消、重做操作…等等. 有了这个方法,就可以很容易的实现网页中的文本编辑器.
如果灵活运用,可以很好的辅助我们完成各种项目.
使用的例子如下:
1、〖全选〗命令的实现
[格式]:document.execCommand(”selectAll”)
[说明]将选种网页中的全部内容!
[举例]在之间加入:
全选
2、〖打开〗命令的实现
[格式]:document.execCommand(”open”)
[说明]这跟VB等编程设计中的webbrowser控件中的命令有些相似,大家也可依此琢磨琢磨。
[举例]在之间加入:
打开
3、〖另存为〗命令的实现
[格式]:document.execCommand(”saveAs”)
[说明]将该网页保存到本地盘的其它目录!
[举例]在之间加入:
另存为
4、〖打印〗命令的实现
[格式]:document.execCommand(”print”)
[说明]当然,你必须装了打印机!
[举例]在之间加入:
打印
execCommand()还有许多其他方面的功能,详细的介绍参考:
http://blog.csdn.net/kntao/article/details/4543123
4.2.3 IE中的事件模拟
从IE8,以及更早版本的IE,都在模仿DOM模拟事件的方式:创建事件对象,初始化事件信息,之后触发事件。当然IE在完成这几个步骤的过程是不同的。
首先不同于dom中创建event对象的方法,IE采用document.createEventObject()方法,并且没有参数,返回一个通用的事 件对象,接下来要对返回的event对象赋值,此时ie并没有提供初始化函数,你只能采用物理方法一个一个的赋值,最后在目标元素上调用 fireEvent()方法,参数为两个:事件处理的名称和创建的事件对象。当fireEvent方法被调用的时候,event对象的 srcElement和type属性将会被自动赋值,其他将需要手动赋值。
示例如下:
var btn = document.getElementById("myBtn");
var event = document.createEventObject();
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;
btn.fireEvent("onclick", event);
这里只是简单的介绍了一些常用的模拟事件,详细的解释可以参考
《Javascript高级程序设计》,里面有更加详细的介绍。
5. IE下单选按钮隐藏后点击对应label无法选中的问题解决
5.1 问题
项目中,有时候填写表单我们的选项会隐藏掉radio或者checkbox,而只显示给用户对应的文字选择,如果用户点击label选择时,在FF/Chrome等标准浏览器中隐藏掉的radio/checkbox也同样随着改变选中状态,而在IE下则不会发生变化。
注意,需要指定表单元素的id属性然后使用label的for属性绑定控件。
代码示例:
<input type="radio" name="gender" id="gender1" value="男" checked="checked" />
<label for="gender1">男</label>
<input type="radio" name="gender" id="gender2" value="女" />
<label for="gender2">女</label>
通过CSS设置display:none 或者 visibility: hidden隐藏掉radio按钮,则当点击label切换选择状态时,对应的按钮实际上是未被改变状态。
5.2 解决方法
方法一:
1.通过javascript脚本来再次操作DOM保证选择状态
$("label").click(function(e){
e.preventDefault();
$("#"+$(this).attr("for")).click().change();
});
方法二(推荐):
不使用display:none,通过position属性定位到可视区域外,避开问题。
position: absolute;
top: -999px;
方法三:
left: -999px;通过z-index/width或透明度opacity将元素隐藏掉。
input{
position: absolute;
z-index: -1;
}
或者是
input{
width: 0;
}
或者是
input{
-webkit-opacity:0;
-moz-opacity:0;
opacity:0;
filter:alpha(opacity:0);
}
需要提醒的是,label的for属性是内联元素,其对应的js属性为htmlFor,例如可以这样访问或设置for属性值。document.getElementById("xxx").htmlFor="inputid";
6. 总结
低版本IE浏览器对于对于网页内容及格式的渲染有着这样或那样的问题,然而为了实现向下的兼容,这些问题又是不可忽略的,不管怎样,能够提前了解这些差异,总会给我们前端的实现带来一些,避免一些不必要的问题。
来源:oschina
链接:https://my.oschina.net/u/2335552/blog/624671