自用前端浏览器兼容方案

272 阅读7分钟

关于浏览器兼容,首先要明确以下几个问题

1.什么是兼容问题/兼容问题的表现是什么?

一句话概括就是:同样的前端代码,在不同品牌、不同内核、同内核不同版本的浏览器中展示的页面效果不一样。

2.存在兼容问题的原因是什么?

  1. 同一浏览器,版本越老,存在 bug 越多,相对于版本越新的浏览器,对新属性和标签、新特性支持越少
  2. 不同浏览器,核心技术(内核)不同,标准不同,实现方式也有差异,最终呈现出来在大众面前的效果也是会有差异
  3. 人为原因,RD写出了不符合W3C标准的代码,有的浏览器支持,有的不支持

3.做兼容的必要性

兼容性一般都是是要的,但需要根据产品的诉求+客户群体+成本的评估确定一个相对合理的兼容范围

4.兼容哪些浏览器

按照最新数据来看,占有量从高到低有: 桌面浏览器:Chrome(Chromium)、Edge(Chromium)、Safari(webkit)、Firefox(gecko)、Opera(Blink) 手机浏览器:Chrome(Chromium)、Safari(webkit)、三星浏览器(Chromium)、UC浏览器(U3内核)

值得一提的是,中国国内还保有大量的xp系统,也就导致ie浏览器,尤其是ie7的保有量也很多

兼容方案

方案路线

有两种路线:

  1. 渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
  2. 优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容

考虑到成本、产品诉求、技术迭代,一般都是选择“优雅降级”的路线

具体方案

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,慎用marginheight尽量补上100%,父级height有定值子级height不用100%,子级全为浮动时底部补个空clear:bothdiv宽尽量用margin,慎用paddingwidth算准实际要的减去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-jsregenerator

JS API兼容

没有合适的工程化手段,需要根据实际问题选用兼容范围最大的api,当然可以自定义eslint规则来校验api的使用

*挑几个当例子说一下就好,不用全列举

问题一:addEventListener 与 attachEvent 区别

  • attachEvent ——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera
  • addEventListener——兼容: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>

常见问题处理技巧

juejin.cn/post/697293…