与IE兼容性纠缠的这几个月 iejqueryhtml5浏览器chrome
程序员文章站
2022-03-17 16:24:52
...
今年过年回来以后,接手一个由公司在其他省市研发部门开发的系统,由于产品刚上线使用,还存在着很多bug,由我和研发部门的同事一起负责修改bug。
这其中就有比较多的IE兼容性问题,由于最初设计者不怎么愿意去迁就IE,最后勉强答应下来至少兼容IE8,结果就是部分页面在IE8下也无法满意。而我们这种行业软件,大多数用户还都是IE7(IE8兼容模式),所以这个问题是避不掉的。花了很多时间来搞,也从中学到些有意思的东西。
内容不分先后主次,想到什么说什么,有的也记不清是IE7还是8,统称旧版IE。
从bootstrap讲起,我们都知道bootstrap是html5和css3的拥护者,这使得如果什么都不处理,在IE78里会出现各种问题。
比如本该在页面两端有两竖条留白,在IE8会被吃掉;比如使用bootstrap的12等分相关class时,IE8完全无效。前者用到的class为.container,后者用到的是诸如col-md-1。因为两者都使用到了css3的@media特性,用以计算width。旧版IE当然不认识啦,当然让旧版IE中实现同样的效果,已经有人帮忙我们做好了。
其中,respond.js正是解决这个问题的关键。另外,html5shiv.js也是必不可少的,其中会帮忙一些兼容问题,具体我没碰到,所以举不出例子。
当然,当你使用bootstrap时,千万别忘了,页面头上的声明
以及head里的声明
以上,算是热热身,很多时候我们开始做系统时,把bootstrap官网的例子先拷过来,那么通常就遇不到以上问题了。我是因为接手系统,事先没想到以上的设置不全,才有所感慨。
1. $("xxx").attr('href')
看起来是个很显而易见的结果,就是把元素a标签里的href属性的值取出来。但是当href值是相对路径时,比如/开头的,高级浏览器依然按部就班的该是什么值给你什么值,而旧版IE会自说自话给你前面加上浏览器地址栏上的url。
2. $("xxx").attr('onclick','window.open(xxxxxxx)')
通常我们给某个元素添加onclick事件么,就直接写在元素上了,又或者可以通过jquery去绑定,而这里至于想通过attr的方式去赋值事件,也是由于项目某个地方的代码逻辑,逼的这里只能这么做。
那么这样做,会有什么问题么?会!在IE7上,事件不会生效!我的猜测是,ie7认为只是给'onclick'这个名字赋了一个'window.openxxxxx'的字符串值。
所以这个地方最佳的方式是通过jquery绑定,但这样做,对另外一个功能有影响,那个功能里,需要复制元素到新的位置,实现是先取出元素html字符串,处理后再append到新位置。因此会导致jquery绑定事件丢失。
于是我很纳闷,开始用很挫的办法,页面头加个IE7判断,里边赋值window.ie = 7, 下面代码判断window.ie === 7,分支内使用jquery绑定,因为此时attr方式失效,最终事件依然绑定了一次,而非两次(如果不加if ie7判断,bind和attr都写,chrome里会触发两次事件)
这里有个有意思的现象,虽然attr在ie7下失效,但是前面说的过程,复制html字符串到新的位置后,新元素的attr居然生效的。这样正好使得,复制后jquery绑定失效,attr又担负起了事件触发的责任。
但是最终还是觉得搓,所以我去重构了那个复制html的逻辑,不再使用html字符串,而是使用jquery对象本身。于是后面就大胆的使用jquery绑定,而不是attr。搞定!
3. console.log, JSON.parse
这个了解的人比较多吧,旧版IE对console对像和JSON对像都不认识。一种比较优雅的办法,就是自己写一组console方法和JSON方法,然后在页面头通过<!--[if lt IE 9]>分支里导入js。
4. html字符串中,尾标签多了少了
由于系统使用backbone来做,需要通过字符串的形式写html模板来构建自定义组件,而一个复杂的字符串html会让你写起来或者读起来吐血。
这里值得称赞的是,ES6和reactJS的字符串模板特性,确实有诚意,虽然scala也早有这种特性。
好吧,我要说的是,我们的代码中,有几个地方都有笔误,多一个</div>, 少一个</div>,<form>写成<from>。。。结果要么导致了$("#xxx")取不到东西,要么导致了css选择器找不到目标,而且都tm发生在旧版IE上,chrome什么的居然都tm能扛过去。
5. z-index
这个绝对是IE7的深坑!坑中之坑!连IE8都不这么玩的。
先说说常规的原理:
忽略了其他样式,就理解为一个div是一个面板。
上面两个div,从z-index 来讲,2要高于1,后面的高于前面的,这是规定,表问为什么
再来看一个:
那么这里谁最高呢?当然大家都会觉得是3,确实是3。
现在我们用IE7来打开上面的UI,会是什么结果呢?div2在最高处!挡住了div3。因为IE7对"后面的高于前面"的原则更强制,由于div3属于div1,因此div2要比1和3两者都要高,无论你div3的z-index多大,天王老子来了也不行。如果你想要让3跑到2之上,你就必须设置div1的z-index大于div2就可以了。
这里一个现实的例子就是。div3的位置是一个下拉列表,所以必须浮在所有东西之上,而恰恰在IE7下,它被div2给挡住了。
看了这篇文章后有所启发 http://www.cnblogs.com/darren_code/archive/2012/03/05/z-index.html
以上,我没有谈到一个关键的专业术语,stack context,只是讲讲表象而已。
6. 旧版IE的大写元素名
如果你在旧版IE中查看元素,你会发现很多都是大写DIV,大写TABLE,即便你页面中写的是小写。首先会在你通过jquery取html字符串处理时造成一点影响,但还不至于诡异。
bootstrap中有个.form-inline的样式,目的是将内部的div都处在一行,使用了display:inline或者inline-block样式。其中有一段css选择器,通过ie开发者工具看是类似
但奇怪的是,页面中.form-inline内部的div却没有受到这个样式影响,每个div依然猖狂的占了一整行。然后,通过查看页面元素,我惊奇的发现,html元素居然都是小写。我忽然想到,这里是通过backbone的组件渲染方式来构建出页面元素的,换句话说,是在浏览器首次加载完页面以后,再把组件元素贴到目标出。这样一来,在backbone组件的字符串模板里写的小写,到了页面上也是保持小写。而使得前面的css失效。
然后我做了个尝试,在页面上自己写了一个css
注意,换成小写了。但tm运行结果一看,被IE又强制转成大写了,无语。最终换了个样式,用了 .form-inline .form-group,虽然不全面,但是够用了
想了想说的差不多了,但是这个过程还在继续。系统中某个变态的页面在IE7下还是一滩浆糊,任重而道远啊卧槽。
这其中就有比较多的IE兼容性问题,由于最初设计者不怎么愿意去迁就IE,最后勉强答应下来至少兼容IE8,结果就是部分页面在IE8下也无法满意。而我们这种行业软件,大多数用户还都是IE7(IE8兼容模式),所以这个问题是避不掉的。花了很多时间来搞,也从中学到些有意思的东西。
内容不分先后主次,想到什么说什么,有的也记不清是IE7还是8,统称旧版IE。
从bootstrap讲起,我们都知道bootstrap是html5和css3的拥护者,这使得如果什么都不处理,在IE78里会出现各种问题。
比如本该在页面两端有两竖条留白,在IE8会被吃掉;比如使用bootstrap的12等分相关class时,IE8完全无效。前者用到的class为.container,后者用到的是诸如col-md-1。因为两者都使用到了css3的@media特性,用以计算width。旧版IE当然不认识啦,当然让旧版IE中实现同样的效果,已经有人帮忙我们做好了。
<!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]-->
其中,respond.js正是解决这个问题的关键。另外,html5shiv.js也是必不可少的,其中会帮忙一些兼容问题,具体我没碰到,所以举不出例子。
当然,当你使用bootstrap时,千万别忘了,页面头上的声明
<!DOCTYPE html>
以及head里的声明
<meta http-equiv="X-UA-Compatible" content="IE=edge">
以上,算是热热身,很多时候我们开始做系统时,把bootstrap官网的例子先拷过来,那么通常就遇不到以上问题了。我是因为接手系统,事先没想到以上的设置不全,才有所感慨。
1. $("xxx").attr('href')
看起来是个很显而易见的结果,就是把元素a标签里的href属性的值取出来。但是当href值是相对路径时,比如/开头的,高级浏览器依然按部就班的该是什么值给你什么值,而旧版IE会自说自话给你前面加上浏览器地址栏上的url。
2. $("xxx").attr('onclick','window.open(xxxxxxx)')
通常我们给某个元素添加onclick事件么,就直接写在元素上了,又或者可以通过jquery去绑定,而这里至于想通过attr的方式去赋值事件,也是由于项目某个地方的代码逻辑,逼的这里只能这么做。
那么这样做,会有什么问题么?会!在IE7上,事件不会生效!我的猜测是,ie7认为只是给'onclick'这个名字赋了一个'window.openxxxxx'的字符串值。
所以这个地方最佳的方式是通过jquery绑定,但这样做,对另外一个功能有影响,那个功能里,需要复制元素到新的位置,实现是先取出元素html字符串,处理后再append到新位置。因此会导致jquery绑定事件丢失。
于是我很纳闷,开始用很挫的办法,页面头加个IE7判断,里边赋值window.ie = 7, 下面代码判断window.ie === 7,分支内使用jquery绑定,因为此时attr方式失效,最终事件依然绑定了一次,而非两次(如果不加if ie7判断,bind和attr都写,chrome里会触发两次事件)
这里有个有意思的现象,虽然attr在ie7下失效,但是前面说的过程,复制html字符串到新的位置后,新元素的attr居然生效的。这样正好使得,复制后jquery绑定失效,attr又担负起了事件触发的责任。
但是最终还是觉得搓,所以我去重构了那个复制html的逻辑,不再使用html字符串,而是使用jquery对象本身。于是后面就大胆的使用jquery绑定,而不是attr。搞定!
3. console.log, JSON.parse
这个了解的人比较多吧,旧版IE对console对像和JSON对像都不认识。一种比较优雅的办法,就是自己写一组console方法和JSON方法,然后在页面头通过<!--[if lt IE 9]>分支里导入js。
4. html字符串中,尾标签多了少了
由于系统使用backbone来做,需要通过字符串的形式写html模板来构建自定义组件,而一个复杂的字符串html会让你写起来或者读起来吐血。
这里值得称赞的是,ES6和reactJS的字符串模板特性,确实有诚意,虽然scala也早有这种特性。
好吧,我要说的是,我们的代码中,有几个地方都有笔误,多一个</div>, 少一个</div>,<form>写成<from>。。。结果要么导致了$("#xxx")取不到东西,要么导致了css选择器找不到目标,而且都tm发生在旧版IE上,chrome什么的居然都tm能扛过去。
5. z-index
这个绝对是IE7的深坑!坑中之坑!连IE8都不这么玩的。
先说说常规的原理:
<div id='1' ></div> <div id='2' ></div>
忽略了其他样式,就理解为一个div是一个面板。
上面两个div,从z-index 来讲,2要高于1,后面的高于前面的,这是规定,表问为什么
再来看一个:
<div id='1' > <div id='3' style='z-index:1000'></div> </div> <div id='2' ></div>
那么这里谁最高呢?当然大家都会觉得是3,确实是3。
现在我们用IE7来打开上面的UI,会是什么结果呢?div2在最高处!挡住了div3。因为IE7对"后面的高于前面"的原则更强制,由于div3属于div1,因此div2要比1和3两者都要高,无论你div3的z-index多大,天王老子来了也不行。如果你想要让3跑到2之上,你就必须设置div1的z-index大于div2就可以了。
这里一个现实的例子就是。div3的位置是一个下拉列表,所以必须浮在所有东西之上,而恰恰在IE7下,它被div2给挡住了。
看了这篇文章后有所启发 http://www.cnblogs.com/darren_code/archive/2012/03/05/z-index.html
以上,我没有谈到一个关键的专业术语,stack context,只是讲讲表象而已。
6. 旧版IE的大写元素名
如果你在旧版IE中查看元素,你会发现很多都是大写DIV,大写TABLE,即便你页面中写的是小写。首先会在你通过jquery取html字符串处理时造成一点影响,但还不至于诡异。
bootstrap中有个.form-inline的样式,目的是将内部的div都处在一行,使用了display:inline或者inline-block样式。其中有一段css选择器,通过ie开发者工具看是类似
.form-inline DIV { display: inline }
但奇怪的是,页面中.form-inline内部的div却没有受到这个样式影响,每个div依然猖狂的占了一整行。然后,通过查看页面元素,我惊奇的发现,html元素居然都是小写。我忽然想到,这里是通过backbone的组件渲染方式来构建出页面元素的,换句话说,是在浏览器首次加载完页面以后,再把组件元素贴到目标出。这样一来,在backbone组件的字符串模板里写的小写,到了页面上也是保持小写。而使得前面的css失效。
然后我做了个尝试,在页面上自己写了一个css
.form-inline div{ display: inline }
注意,换成小写了。但tm运行结果一看,被IE又强制转成大写了,无语。最终换了个样式,用了 .form-inline .form-group,虽然不全面,但是够用了
想了想说的差不多了,但是这个过程还在继续。系统中某个变态的页面在IE7下还是一滩浆糊,任重而道远啊卧槽。