浏览器兼容性是一个很长久的话题,之所以前端需要面对浏览器兼容性,是因为用户的环境有不同的平台,不同的浏览器。不同的厂商之间为了相互竞争,对标准的实现不一样。不同的浏览器有不同的内核。即使同一个浏览器也有不同的版本,不同的版本对同一特性的支持情况也不尽相同。也可能某个浏览器的某个版本针对某个特性存在bug,而我们的产品又暂时不能放弃这个版本的用户,所以必须兼容。
PC的情况是这样,移动端的情况更复杂,除了要适配不同的平台和浏览器,还需要适配不同的屏幕,不同的硬件。
而要写出兼容性良好的代码,一是要了解规范,了解每个特性的实现原理,二是要了解各个浏览器内核的差异,三是要多尝试多踩坑,多积累。
下面介绍一下浏览器兼容性问题的大致分类,更多的细节可以到w3help查询。
html
目前主要是一些浏览器对html5的新特性不能完全支持,在使用html5新特性之前,可以在caniuse上查询特性的支持情况,也可以在html5test这个网站查询各个浏览器对html的支持情况。
如果想在不支持html5的浏览器里继续使用html的标签,可以用html5shiv
css
css的兼容性比较多,而且复杂,都是细节的问题,这里做一些整理。关于css选择器和各个属性的兼容性情况可以在http://www.quirksmode.org/css/查询。一些比较常见的css兼容性如下:
盒模型的解释

border-box:width=content + l/r padding + l/r border(IE盒模型)
content-box:width = content-width(标准盒模型)
新特性的支持
市场上的浏览器对css3的特性支持不够完整,或者不同的厂商对同一个属性的实现不一样,所以出现了浏览器前缀。常见的浏览器前缀
- Firefox:-moz-
- Safari:-webkit-
- Opera:-o-box-
- IE:-ms-box-
需要添加浏览器前缀的属性有:
一些例子收集
1 2 3 4 5
| .transparent{ filter:alpha(opacity=60); /*支持 IE 浏览器*/ -moz-opacity:0.6; /*支持 FireFox 浏览器*/ opacity:0.6; /*支持 Chrome, Opera, Safari 等浏览器*/ }
|
- IE6以前的版本会把
text-align:center
转换为<center></center>
元素原生js
浏览器对原生js的支持情况可以通过http://kangax.github.io/compat-table/es5/查询
- IE8不支持数组的indexOf方法,可以如下方法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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; }; }
|
- new Date().getYear(),在FF里返回的年份是减去1900
- 对象字面量最后一个属性之后不支持,如下代码,在IE6/7下会报错
1 2 3 4
| var obj = { name:'ggbond', }
|
1 2 3 4 5 6
| var ary = [1,2,3,] // IE 8 var ary = [1,2,3,null]
|
来源
DOM
事件类
事件绑定
1 2 3 4 5 6 7 8 9
| addHandler:function(element,type,handler){ if(element.addEventListener){//检测是否为DOM2级方法 element.addEventListener(type, handler, false); }else if (element.attachEvent){//检测是否为IE级方法 element.attachEvent("on" + type, handler); } else {//检测是否为DOM0级方法 element["on" + type] = handler; } }
|
取消绑定
1 2 3 4 5 6 7 8 9
| removeHandler:function(element, type, handler){ if (element.removeEventListener){ element.removeEventListener(type, handler, false); } else if (element.detachEvent){ element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } }
|
事件对象
1 2 3 4 5 6 7
| getEvent: function(event){ return event ? event : window.event; }, //获取事件对象目标的兼容性写法 getTarget: function(event){ return event.target || event.srcElement; }
|
取消默认事件
1 2 3 4 5 6 7
| preventDefault: function(event){ if (event.preventDefault){ event.preventDefault(); } else { event.returnValue = false; } }
|
取消冒泡
1 2 3 4 5 6 7
| stopPropagation: function(event){ if (event.stopPropagation){ event.stopPropagation(); } else { event.cancelBubble = true; } }
|
鼠标对象
1 2 3 4 5 6 7 8 9 10 11
| getRelatedTarget: function(event){ if (event.relatedTarget){ return event.relatedTarget; } else if (event.toElement){ return event.toElement; } else if (event.fromElement){ return event.fromElement; } else { return null; } }
|
触发自定义事件
1 2 3 4
| document.createEvent() event.initEvent() element.dispatchEvent()
|
DOM
IE提供的children、childNodes和firefox下的childNodes的行为是有区别的,firefox下childNodes会把换行和空白字符都算作父节点的子节点,而IE的childNodes和children不会
1 2 3 4 5 6 7 8 9
| function getStyle(obj,name){ if(obj.currentStyle){ return obj.currentStyle[name]; } else{ return getComputedStyle(obj,false)[name]; } }
|
XMLHttpRequest
1 2 3 4 5 6 7
| var xmlhttp; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE的获取方式 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
|
classList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| if (!("classList" in document.documentElement)) { Object.defineProperty(HTMLElement.prototype, 'classList', { get: function() { var self = this; function update(fn) { return function(value) { var classes = self.className.split(/\s+/g), index = classes.indexOf(value); fn(classes, index, value); self.className = classes.join(" "); } } return { add: update(function(classes, index, value) { if (!~index) classes.push(value); }), remove: update(function(classes, index) { if (~index) classes.splice(index, 1); }), toggle: update(function(classes, index, value) { if (~index) classes.splice(index, 1); else classes.push(value); }), contains: function(value) { return !!~self.className.split(/\s+/g).indexOf(value); }, item: function(i) { return self.className.split(/\s+/g)[i] || null; } }; } }); }
|
其他
cookie大小
- IE50个,每个最长4095
- FF 50个,每个最长4097
针对同一个域名同时发起的http请求数

图片来源自:stevesouders
后续遇到兼容性会问题,会持续更新这篇文章