浏览器常见的兼容性问题以及解决方案

1. 前言

本文前半部分将主要展示各主流浏览器的内核版本情况,HTML5以及CSS3的支持情况,以及开发过程中常见的兼容性问题。后半部分则将会从工程化的角度上来考虑如何解决这些问题,并提出向下兼容的方案。

Tips:文档内 “+” 均表示大于等于,如 IE9+ 即代表 IE 版本号大于等于 9。

2. 国内主流浏览器内核版本

对于国内的情况而言,大多数用户会使用主流大厂提供的浏览器,同时对于部分机房环境则使用了系统默认的 IE 浏览器,因此在下面的列表中整理了IE浏览器以及主流国产浏览器对于浏览器内核的版本使用情况,同时附带了各浏览器版本的发布时间:

Tips: 部分国产浏览较早之前的浏览器使用了 Webkit 内核,但是由于受 Chrome 转向使用了 Blink 内核的影响,因此国产浏览器也逐渐放弃使用了 Webkit 内核,目前为止,国内主流的浏览器均使用了 Chromium 内核来作为底层内核(引用参考),只是内核版本不同。

  • Internet Explore
    • IE6 2001
    • IE7 2006(Windows XP SP3、Windows Vista)
    • IE8 2009(Windows7)
    • IE9 2011
    • IE10 2011(Windows 8)
    • IE11 2013(Windows8.1、Windows 10)
  • QQ 浏览器(基于Chromium开发,针对IE内核优化)
    • v10.0 2020(Chromium70 & IE 10)
    • v9.6 2017(Chromium53)
    • v9.3 2016(Chromium47)
    • v9.0 2015(Chromium43 & IE 8)
    • v7.0 2012(Webkit)
  • 360极速浏览器(双核)
    • v12.0 2020 (Chromium78)
  • 360安全浏览器(双核)
    • v12.1 2020 (Chromium78)
    • v6.0 2015(Chromium45)
  • 搜狗浏览器(双核)
    • v8.0 2020 (Chromium65)
  • 猎豹浏览器(双核)
    • v6.5 2017(Chromium57)
  • Edge
    • 旧版本 (Trident 7 / IE 11)
    • 2020 年正式版(Chromium83)

3. Javascript 兼容性问题

网络相关

XMLHttpRequest

对于原生 XMLHttpRequest 支持 IE6+ 的浏览器,如果需要在 IE6 上支持 XMLHttpRequest 对象,则需要使用 ActiveXObject 对象做兼容。

关于跨域,IE10+ 才支持 ResponseHeader Access-Control-Allow-Origin(完整兼容性参考),低版本浏览器可以使用 JSONP 方案进行跨域。

关于基于 XMLHttpRequest 而构建的 Axios 使用了 Promise,因此在不支持 ES6 版本的浏览器上使用时需要添加 Promise 的语法垫片。

Fetch API

Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,需要注意其实一个实验性特征,且 IE 所有版本均不支持。

Websocket

在 IE10+ 支持 WebSocket 对象,并且与其他浏览器一样遵循了 Standard - RFC 6455 Support 协议。对于低版本的浏览器,SocketIO Client 端提供了 IE9 的解决方案(实现原理是在 WebSocket、AJAX long-polling、AJAX multipart streaming、 Forever Iframe 几种方式之间进行切换)。

DOM 相关

document.all

仅支持 IE 浏览器,该特性已经从 Web 标准中删除,请尽量不要使用该特性。

HTMLElement.childNodes

IE8 以及以下版本浏览器获仅获取元素节点,更高版本的浏览器以及现代浏览器获取包含文本节点的所有子节点。

事件相关

window.event

window.event 作为 IE10 以及以下版本的 IE 浏览器获得事件对象的方法,在 IE11 中已经被移出,该方法是一个非标准化的方法。

addEventListener

IE9 以下版本的浏览器不支持 addEventListener 方法添加事件,需要使用 attachEvent 来添加。同时需要注意,使用 attachEvent 方法有个缺点,this 的值会变成 window 对象的引用而不是触发事件的元素。

图像相关

window.requestAnimationFrame

在 IE9 以及以下版本的浏览器上可以使用定时器来解决 requestAnimationFrame 的兼容性问题,但这也就意味着会失去其带来的最佳的动画流畅度的优势。

Web Animations API

Web Animations API 提供给了开发人员使用 JS 来操作动画的能力,但是目前该 API 的标准仍处于草案阶段,对浏览器的版本依赖较高(其中 Chrome 不完全支持,PC Safari 在 2020 年 5 月份的更新中完成了对 API 的全支持,IE 则完全不支持),因此目前阶段不推荐在生产环境下使用。

WebGL

WebGL1 兼容性:

WebGL2 兼容性:

WebGL 经常用于 3D Canvas,尽管 WebGL1 可以在 IE11 运行,但是需要注意的是前端最常用的 3D 模型渲染库 three.js 从 R95 版本起使用 WebGL2 环境来进行渲染,因此在 IE 浏览器上会出现兼容性问题。

其他

window.getComputedStyle()

IE9+ 支持 getComputedStyle() 获取行外样式,可以使用 currentStyle() 兼容低版本浏览器。

Date.prototype.getYear()

getYear() 方法返回指定的本地日期的年份。因为 getYear 不返回千禧年[full years] (“year 2000 problem”),所以这个方法不再被使用,现在替换为 getFullYear。仅在 IE8 以及以下的浏览器可以获取到正常的年份。

通过索引获取字符串某个字符

IE7 以及以下浏览器不兼容类似 str[1] 的写法,可以使用 charAt() 方法来由索引获取字符。

WebWorker API

ES6 语法

在现代浏览器中,已经基本可以使用 ES6 标准的语法,同时在 Chrome 80、Firefox 67、Safari 11.1 版本以上支持了动态 import 引入模块。但是在生产环境中使用 ES6 语法并不进行转义仍是一种不合适的做法。

Babel

Babel 作为前端工程化的代码转义器,是目前让 ES6 语法兼容低版本浏览器的主要解决方案。引入 Babel 到构建工具中后,Babel 可以支持 ES6 语法(如:Class 语法、箭头函数、解构等)转 ES5 语法,让低版本浏览器可以正常解析。

但是对于 ES6 新增对象,如 Promise、Map、Set 等,原生 Babel 不能对其进行转义。为了支持新对象,Babel 提供了 babel-runtime 与 babel-polyfill 两种解决方案:

  • runtime 主要是在需要使用 ES6 对象时将其作为模块引入,这样不会造成全局变量的污染;
  • 而所谓的 polyfill 即语法垫片,则是将可能使用到的 ES6 对象在全局环境下进行声明并使用 ES5 语法进行了功能实现,这样可能会造成全局变量的污染,并且编译后 polyfill 将会占据很大空间,因此不推荐在方法类库中使用。

@babel/preset-env

在 Babel7 后,官方推荐使用 @babel/preset-env 作为预设方案,开发者可以通过配置所需的浏览器的最低版本,就会自动在项目中引入所需的 plugin,来兼容语法。

以 Webpack 为例,当我们采用了如下的配置后,@babel/preset-env 最终转义得到的代码会兼容 IE8 以上的浏览器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
targets: {
browsers: ["ie >= 8"]
},
useBuiltIns: "usage",
corejs: "3.6.5"
}
]
]
}
},
"eslint-loader"
]
}

在 IE8 环境下,由于 ES3 不允许使用保留字作为对象的键值,因此会出现代码报错(引用参考)。

4. CSS 属性兼容性问题

选择器

引用参考

基本选择器

基本选择器包含了通用选择器(*)、类型选择器、类选择器、ID 选择器、属性选择器([attr=value]),他们在 IE7+ 的浏览器环境下都得到了比较好的适配。但是关于兼容性,仍需要注意以下两点:

  1. 对于可以使用 命名空间 的选择器,只有 IE9+ 以上的浏览器才可以使用。
  2. 属性选择器不区分大小写的修饰符(i)不能在 IE 浏览器上使用,区分大小写的修饰符(s)只能在 Firefox66 上使用(引用参考)。

组合器

组合器包含了后代组合器、直接子代组合器(>)、一般兄弟组合器(~)、紧邻兄弟组合器(+)。组合器需要在 IE7+ 以上版本的浏览器使用,更低版本的浏览器则不支持。

伪类

引用参考

伪类选择器的兼容情况通常比较复杂,在 CSS3 规范实现不标准的 IE 浏览器上会出现不兼容的情况,以下是对兼容性的概览:

支持比较好的伪类选择器 (IE4+):

  1. :active
  2. :hover
  3. :link
  4. :visited

部分支持的伪类选择器:

选择器 兼容版本 备注
:checked IE9+
:focus IE8+
:default 非 IE 浏览器
:disabled IE9+ IE 浏览器不支持 <fieldset> 标签上的 disabled 状态识别
:indeterminate IE10+
:first IE8+
:not IE9+
:first-child / :last-child IE9+ 对于 IE8 及更早版本的浏览器中的 :first-child,必须声明 <!DOCTYPE>
:first-of-type / :last-of-type IE9+ IE浏览器会将所有未知元素视为相同的元素类型
:last-child / :first-child IE9+
:nth-child() / :nth-last-child() IE9+ IE浏览器不支持匹配没有父元素的元素
:nth-of-type() / :nth-last-of-type() IE9+ IE浏览器会将所有未知元素视为相同的元素类型
:only-child IE9+ IE浏览器不支持匹配没有父元素的元
:only-of-type IE9+ IE浏览器会将所有未知元素视为相同的元素类型
:optional IE10+
:out-of-range 非 IE 浏览器
:root IE9+
:valid IE10+ IE浏览器无法适用于 <form> 元素
:target IE9+

伪元素

CSS3 规范中规定伪元素要使用双冒号 :: 来与伪类做区分,但 IE8 以及以下的浏览器不支持这一规范,只能使用 :

伪元素 兼容版本 备注
::after IE8+ IE浏览器不支持动画和过渡支持
::before IE8+ IE浏览器不支持动画和过渡支持
::first-letter IE5.5+ 不支持荷兰文
::first-line IE5.5+
::selection IE9+ ::selection CSS伪元素选择器是CSS第3级选择器的草案,但是在被推荐使用前就被废弃。它现在在第4级伪元素选择器草案中。
::slotted() 非 IE 浏览器

其他

媒体查询器

媒体查询器的支持情况比较复杂,但是常用于做页面响应式的 width 查询(width media feature)仅在 IE9+ 的浏览器环境下运行,引用参考 中可以查看到完整的媒体查询器的兼容情况。

@page

@page 规则用于在打印文档时修改某些CSS属性。你不能用@page规则来修改所有的CSS属性,而是只能修改margin,orphans,widow 和 page breaks of the document。对其他属性的修改是无效的。

5. HTML 标签兼容性问题

HTML5 标签支持

关于 HTML5 标签,大部分常用的语义化标签如 nav、article 等,都可以在 IE9+ 的浏览器上正常被解析,因此这些标签不再进行一一列举,下文主要罗列出 HTML5 新增的功能性组件的兼容性情况。

template

HTML内容模板 <template> 元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以(原文为 may be)在运行时使用JavaScript实例化。

video

HTML <video> 元素 用于在HTML或者XHTML文档中嵌入媒体播放器,用于支持文档内的视频播放。

embed

HTML <embed> 元素将外部内容嵌入文档中的指定位置。此内容由外部应用程序或其他交互式内容源(如浏览器插件)提供。

不同浏览器之间显示有差异。Blink 内核浏览器(Chrome,Opera)会显示 HTML 资源的内容,但 Firefox 会显示一条通知消息,指出内容需要一个插件(见 bug 730768)。建议使用 <object><iframe> 元素。

audio

HTML <audio> 元素用于在文档中表示音频内容。 <audio> 元素可以包含多个音频资源, 这些音频资源可以使用 src 属性或者<source> 元素来进行描述; 浏览器将会选择最合适的一个来使用。对于不支持 <audio> 元素的浏览器,<audio> 元素也可以作为浏览器不识别的内容加入到文档中。

track

<video><audio> 这类媒体元素指定文本轨道(字幕)。(MDN)

canvas

<canvas> 元素可被用来通过JavaScript(Canvas API 或 WebGL API)绘制图形及图形动画。

svg

定义一个嵌入式矢量图。

datalist

代表提供给其他控件的一组预定义选项。(MDN)

output

代表计算值。(MDN)

process

代表进度条。(MDN)

Meter

HTML <meter> 元素用来显示已知范围的标量值或者分数值。(MDN)