关于浏览器兼容,首先要明确以下几个问题
1.什么是兼容问题/兼容问题的表现是什么?
一句话概括就是:同样的前端代码,在不同品牌、不同内核、同内核不同版本的浏览器中展示的页面效果不一样。
2.存在兼容问题的原因是什么?
- 同一浏览器,版本越老,存在
bug
越多,相对于版本越新的浏览器,对新属性和标签、新特性支持越少 - 不同浏览器,核心技术(内核)不同,标准不同,实现方式也有差异,最终呈现出来在大众面前的效果也是会有差异
- 人为原因,RD写出了不符合W3C标准的代码,有的浏览器支持,有的不支持
3.做兼容的必要性
兼容性一般都是是要的,但需要根据产品的诉求+客户群体+成本的评估确定一个相对合理的兼容范围
4.兼容哪些浏览器
按照最新数据来看,占有量从高到低有: 桌面浏览器:Chrome(Chromium)、Edge(Chromium)、Safari(webkit)、Firefox(gecko)、Opera(Blink) 手机浏览器:Chrome(Chromium)、Safari(webkit)、三星浏览器(Chromium)、UC浏览器(U3内核)
值得一提的是,中国国内还保有大量的xp系统,也就导致ie浏览器,尤其是ie7的保有量也很多
兼容方案
方案路线
有两种路线:
- 渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
- 优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容
考虑到成本、产品诉求、技术迭代,一般都是选择“优雅降级”的路线
具体方案
HTML+ CSS兼容
举措一:使用PostCSS
属于工程化手段。在webpack中使用PostCSS,这是一个用 JavaScript 工具和插件转换 CSS 代码的工具。
- 利用从 Can I Use 网站获取的数据为 CSS 规则添加特定厂商的前缀。 Autoprefixer 自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀。
- 帮你将最新的 CSS 语法转换成大多数浏览器都能理解的语法。
举措二:使用css hack
hack是一种技巧,借助不同css属性前缀,让不同浏览器识别,从而实现适配目的。
使用hack 可以根据浏览器对hack符号的认知,把浏览器分为3类:
- ie6;
- ie7和遨游;
- 其他(ie8 chrome ff safari opera等)
它们的对hack符号的认知:
ie6
认识的hack
是下划线_
和星号*
ie7
遨游认识的hack
是星号*
(包括上面问题6中的!important
也算是hack
的一种。不过实用性较小。)
规则:
比如这样一个css
设置height:300px;
*height:200px;
_height:100px;
ie6
浏览器在读到 height:300px
的时候会认为高时300px;继续往下读,他也认识*heihgt
, 所以当ie6
读到*height:200px
的时候会覆盖掉前一条的相冲突设置,认为高度是200px
。继续往下读,ie6
还认识_height
,所以他又会覆盖掉200px高的设置,把高度设置为100px
ie7
和遨游也是一样的从高度300px的设置往下读。当它们读到*height200px
的时候就停下了,因为它们不认识_height
。所以它们会把高度解析为200px;剩下的浏览器只认识第一个height:300px;
所以他们会把高度解析为300px。
CSS hack示例:
/* CSS属性级Hack */
color:red; /* 所有浏览器可识别*/
_color:red; /* 仅IE6 识别 */
*color:red; /* IE6、IE7 识别 */
+color:red; /* IE6、IE7 识别 */
*+color:red; /* IE6、IE7 识别 */
[color:red; /* IE6、IE7 识别 */ color:red\9; /* IE6、IE7、IE8、IE9 识别 */color:red\0; /* IE8、IE9 识别*/color:red\9\0; /* 仅IE9识别 */color:red \0; /* 仅IE9识别 */color:red!important; /* IE6 不识别!important 有危险*/
/* CSS选择符级Hack */
*html #demo { color:red;} /* 仅IE6 识别 */
*+html #demo { color:red;} /* 仅IE7 识别 */
body:nth-of-type(1) #demo { color:red;} /* IE9+、FF3.5+、Chrome、Safari、Opera 可以识别 */
head:first-child+body #demo { color:red; } /* IE7+、FF、Chrome、Safari、Opera 可以识别 */
:root #demo { color:red\9; } : /* 仅IE9识别 */
举措三:编写标准头,对padding,marign,height,width作统一的初始设置
写好标准头http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> http://www.w3.org/1999/xhtml”>
尽量用padding
,慎用margin
,height
尽量补上100%,父级height
有定值子级height
不用100%,子级全为浮动时底部补个空clear:both
的div
宽尽量用margin
,慎用padding
,width
算准实际要的减去padding
举措四:快捷处理float的div闭合
将以下代码加入Global CSS 中,给需要闭合的div加上class="clearfix"即可:
/* Clear Fix */
.clearfix:after {
content:".";
display:block;
height:0;
clear:both;
visibility:hidden;
}
.clearfix {
display:inline-block;
}
/* Hide from IE Mac */
.clearfix {
display:block;
}
/* End hide from IE Mac */
/* end of clearfix */
JS语法兼容
举措一:babel-loder + @babel/preset-env + polyfill
属于工程化手段。
- 安装并在webpack配置
babel-loder
和@babel/preset-env
,可以实现对部分ES6语法的转化; - 安装配置
@babel/polyfill
可以对ES6中高级语法进行转化,如Promise,async,await等,一般需要使用polyfill下的core-js
和regenerator
JS API兼容
没有合适的工程化手段,需要根据实际问题选用兼容范围最大的api,当然可以自定义eslint规则来校验api的使用
*挑几个当例子说一下就好,不用全列举
问题一:addEventListener
与 attachEvent
区别
attachEvent
——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、operaaddEventListener
——兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8
兼容方案
function addEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) { // W3C标准
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent) { // IE
var r = elm.attachEvent('on' + evType, fn); // IE5+
return r;
} else {
elm['on' + evType] = fn; // DOM事件
}
}
问题二: document.formName.item("itemName")
的问题
- 兼容IE:
document.formName.item("itemName")
或document.formName.elements ["elementName"]
- 兼容FireFox:
document.formName.elements["elementName"]
兼容方案
//统一使用
document.formName.elements["elementName"]
问题三:集合类对象问题
- IE下,可以使用 () 或 [] 获取集合类对象;
- Firefox下,只能使用 [ ] 获取集合类对象。
兼容方案
统一使用 [] 获取集合类对象
问题四:自定义属性问题
- IE下,可以使用获取常规属性的方法来获取自定义属性,也可以使用 getAttribute() 获取自定义属性;
- Firefox下,只能使用getAttribute() 获取自定义属性。
兼容方案
统一通过 getAttribute() 获取自定义属性。
问题五:eval(“idName”) 的问题
- IE下,可以使用 eval(“idName”) 或 getElementById(“idName”) 来取得 id 为 idName的HTML对象;
- Firefox下,只能使用 getElementById(“idName”) 来取得 id 为 idName 的HTML对象。
兼容方案
统一用 getElementById(“idName”) 来取得 id 为 idName 的HTML对象。
问题六:变量名与某HTML对象ID相同的问题
- IE下,HTML对象的ID可以作为 document 的下属对象变量名直接使用,Firefox下则不能;
- Firefox下,可以使用与HTML对象ID相同的变量名,IE下则不能。
兼容方案
使用 document.getElementById(“idName”)代替 document.idName。最好不要取HTML对象ID相同的变量名,以减少错误;在声明变量时,一律加上 var 关键字,以避免歧义
问题七:input.type属性问题
- IE下 input.type 属性为只读
- Firefox下 input.type 属性为读写
兼容方案
不修改 input.type 属性。如果必须要修改,可以先隐藏原来的 input,然后在同样的位置再插入一个新的 input 元素
问题八:window.event 问题
window.event 只能在IE下运行,而不能在Firefox下运行,这是因为Firefox的 event 只能在事件发生的现场使用
兼容方案
在事件发生的函数上加上 event
参数,在函数体内(假设形参为 evt
)使用 var myEvent = evt?evt:(window.event? window.event:null)
<input type="button" onclick="doSomething(event)"/>
<script language="javascript">
function doSomething(evt) {
var myEvent = evt ? evt :(window.event ? window.event : null);
…
}
</script>