网络日志

几个处理CSS兼容性的实用方式

今天我们来聊一聊如何处理CSS兼容性所带来的问题。

因为浏览器厂商众多,采用的浏览器内核各不相同,所以对CSS语法支持的程度也各不相同。

有的可能是语法不支持,有的可能是语法支持但是效果表现形态不同,反正都是因为浏览器不同所造成的。

(要想知道各大浏览器的市场占有率,大家可以访问度娘家的统计网站)

了解了占有率后,还要知道各个CSS样式在这些浏览器中的一个支持程度是怎样的,可以访问CANIUSE—前端兼容性自查工具或者quirksmode

下面就来一一介绍各种CSS兼容性情况及解决手段。

浏览器的不同默认行为

直接举例:

在IE10以下的浏览器中,给图片添加链接,默认会产生一个蓝色的边框,而高级浏览器却没有这个现象。

所以需要给img{ border-style: none; }来解决这个CSS兼容性问题。

这种类似的问题非常多,幸好有一个专门的库,可以解决大部分这样的兼容性问题,即:Normalize CSS

Normalize CSS 可以看成是一种 Reset CSS 的替代方案。

创造Normalize CSS有下面这几个目的:

  • 保护有用的浏览器默认样式,而不是完全去掉它们
  • 一般化的样式:为大部分HTML元素提供
  • 修复浏览器自身的bug并保证各浏览器的一致性
  • 优化CSS可用性:用一些小技巧
  • 解释代码:用注释和详细的文档来

所以强烈建议在开发网页的时候,首先要引入Normalize CSS,然后再进行具体的样式编写

有时候浏览器的默认行为可通过:添加浏览器前缀的方式进行解决。

先来了解下什么是浏览器前缀。

我们都知道一个CSS样式从提案到正式发布是需要漫长时间的,而浏览器厂商为了提前满足市场需求,就通过添加浏览器前缀的方式提前去支持这些样式,这样等样式正式发布的时候,也不影响正常的使用。

常见的浏览器前缀有:Chrome和Safari的-webkit-、Firefox的-moz-、IE的-ms-等。

  • 比如在IOS下,切换横屏的时候字体会自动变大,可以通过给html{ -webkit-text-size-adjust: 100%; 来解决这个问题。
  • 再比如输入框在IE下会有一个关闭的图片号,可以通过给input[type=text]::-ms-clear{ display: none; }来解决这个问题。

有时候不同浏览器下的默认样式是没办法统一的,例如表单的一些元素,如:复选框、单选框、下拉菜单等。

这时就需要完全模拟样式才能解决。

下面是模拟的复选框实现方案代码:

<style>
.checkbox{
    width: 20px;
    height: 20px;
    display: inline-block;
    overflow: hidden;
}
.checkbox input{
    display: none;
}
.checkbox div{
    width: 100%;
    height: 100%;
    border: 1px #767676 solid;
    border-radius: 2px;
    box-sizing: border-box;
}
.checkbox input:checked + div{
    background: #0075ff;
    display: flex;
}
.checkbox input:checked + div::after{
    content: "";
    margin: auto;
    width: 10px;
    height: 4px;
    border:1px white solid;
    border-top:none;
    border-right:none;
    transform: rotate(-45deg);
    position: relative;
    top: -2px;
}
</style>
<body>
<label class="checkbox">
    <input type="checkbox">
    <div></div>
</label>
</body>

CSS Hack

有时候我们需要为不同的浏览器甚至不同版本编写特定的 CSS样式,这个过程被称为 CSS hack!

CSS hack的书写方法大致可归纳为以下几种:条件hack,属性hack,选择器hack。

条件hack是 HTML 源码中被 IE 有条件解释的语句。

条件hack可被用来向 IE 提供及隐藏代码。

使用了条件hack的页面在 IE9 中可正常工作,但在 IE10 中无法正常工作,IE10不再支持条件hack。

<!--[if IE 8]>
<link href="ie8only.css" rel="stylesheet">
<![endif]-->

属性hack:在CSS样式属性名称前面加上一些hack前缀,这只能被特定的浏览器识别。

  • _:选择IE6或更低。此外,还可以使用连线(中划线)(-),以避免与某些划线的属性相混淆,因此最好使用下划线(_)
  • *:选择IE7或更低。例如:(+)和(#)等,虽然行业对(*)的认知程度较高
  • \9:选择IE6+
  • \0:选择IE8+和Opera15以下的浏览器
.box{
    color: #090\9; /* IE8+ */
    *color: #f00;  /* IE7 */
    _color: #ff0;  /* IE6 */
}

选择器hack:对于网页表现不一致或需要特别对待的浏览器,在CSS选择器之前加上一些前缀,只有特定的浏览器才能识别。

* html .box { color: #090; }       /* For IE6 */
* + html .box { color: #ff0; }     /* For IE7 */

渐进增强与优雅降级

渐进增强是针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

优雅降级是一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

举例说明,比如在高级浏览器中支持边框圆角(border-radius),而低版本浏览器不支持边框圆角,这时采用直角方式,这就是优雅降级,当然也可以采用图片模拟圆角的形式,也是属于优雅降级。

再比如高级浏览器支持阴影(box-shadow),而低版本浏览器不支持阴影,这时只是采用普通的边框代替,属于优雅降级。

简单来说,低版本浏览器的主要功能不受影响,布局没有严重的错乱问题即可。不用非要跟高级浏览器完全一致,这种思想便是优雅降级。一般可采用样式是否支持来进行覆盖操作,具体举例代码如下:

<style>
.box {
    width: 200px;
    height: 200px;
    box-shadow: 0 1px 3px rgba(0,0,0,.25);
    border: 1px solid #d0d0d5;
    border: 0 rgba(0,0,0,.2);
}
</style>
<body>
<div class="box"></div>
</body>

由于IE8浏览器不认识rgba颜色表示,因此,在IE8眼中,border: 0 rgba(0,0,0,.2); 这种写法就是不合法的,就会被忽略。

Polyfill(垫片)处理CSS兼容

Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。

简单来说就是通过JavaScript方式来解决CSS兼容性问题,通常需要引入一个JavaScript文件。

下面介绍一些常见的处理CSS兼容性的Polyfill:

  • SELECTIVIZR(让IE6-9支持CSS3样式)
  • transform(让IE支持transform变形)
  • Respond(让IE6-8支持媒体查询)
  • html5shiv(让IE识别并支持HTML5元素)
  • css-vars-ponyfill(支持CSS变量)

这里我们只举一个例子,举一反三就可以了。

Respond.js是让让IE6-8支持媒体查询,首先先去下载相关的JS文件,可以通过github进行下载,即:respond.src.js文件。

Respond.js在使用的时候,有一些要求:

  1. 需要启动本地服务器(localhost),不能使用普通本地的url地址(file://开头);
  2. 需要外部引入CSS文件,将CSS样式书写在style中是无效的;
  3. 由于respond插件是查找CSS文件,再进行处理,所以respond文件一定要放置在CSS文件的后面。

    <style>
    .box{
     width: 100px;
     height: 100px;
     background: pink;
    }
    </style>
    <link rel="stylesheet" href="./test.css"  media="screen and (max-width: 480px)">
    <script src="./respond.src.js"></script>
    </head>
    <body>
     <div class="box"></div>
    </body>
    /* test.css */
    .box{
     background: skyblue;
    }

postcss(工程化)处理CSS兼容

前面介绍过的一些解决方案,像添加浏览器前缀,优雅降级,JavaScript处理CSS兼容等等,一般都需要手动去完成,而postcss是一种工程化的方式去解决这些兼容性,从而达到自动化的处理。

PostCSS 是一个允许使用 JS 插件转换样式的工具。

这些插件可以检查(lint)你的 CSS,支持 CSS Variables 和 Mixins, 编译尚未被浏览器广泛支持的先进的 CSS 语法,内联图片,以及其它很多优秀的功能。

常见的利用postcss来解决CSS兼容性的插件非常多,这里介绍一些:

  • Autoprefixer(自动化需要的属性添加浏览器厂商的私有前缀)
  • postcss-color-rgba-fallback(IE8不支持rgba()颜色,转换成十六进制)
  • postcss-opacity(给IE浏览器添加滤镜属性,作为降级处理)
  • postcss-pseudoelements(让IE8中不仅支持一个冒号:,也支持::的伪元素)
  • postcss-vmin(让IE9支持viewport相对单位vmin)
  • node-pixrem(让IE10以下不支持rem单位转成px单位)
  • postcss-cssnext(让CSS高级新语法得到支持,优雅降级)

这里我们只举一个例子,举一反三就可以了。

postcss-cssnext能够让CSS高级新语法得到支持,原理就是对于不支持新语法的浏览器进行语法降级处理。

postcss可以在前端工具webpack、gulp中进行集成,也可以单独通过nodejs环境进行使用。

  1. 需要nodejs环境
  2. 编写postcss.config.js文件进行任务转换

    const cssnext = require('postcss-cssnext');
    module.exports = {
     plugins: [
         cssnext
     ]
    };
  3. 编写源文件 src/demo.css

    /* src/demo.css */
    :root{
     --color: pink;
    }
    .box{
     color: var(--color);
     background-color: var(--color);
     border: 1px var(--color) solid;
    }

4、通过命令:postcss src/demo.css -o dist/demo.css -w

/* dist/demo.css */
.box{
    color: pink;
    background-color: pink;
    border: 1px pink solid; 
}

移动端CSS兼容

移动端的CSS兼容性还是蛮多的,这里给大家列举一些常见的兼容处理,首先就是移动端1px的问题。要了解这个问题,首先需要了解什么是逻辑像素和物理像素。

逻辑像素与物理像素

逻辑像素,也叫“设备独立像素”。

对于前端来说就是css中的像素,举例:iphone6下的逻辑像素为375px。

物理像素,即设备屏幕实际拥有的像素点。

一个设备生产出来,他们的像素就已经确定了,举例:iphone6下的物理像素为750px。

可以发现iphone6下,其物理像素是逻辑像素的2倍,可用“设备像素比”来表示这个比值(即物理像素除以逻辑像素的值),可通过JavaScript代码window.devicePixelRatio来获取设备像素比。

那究竟逻辑像素与物理像素的关系是什么呢?

这里首先先确定什么是相对单位,什么又是绝对单位。

像m这种绝对单位,定义是什么:米的长度等于氪-86原子的2P10和5d1能级之间跃迁的辐射在真空中波长的1650763.73倍。查到的m的定义如上,也就是说在现实世界中,m是一个固定的长度。

px全称为pixel,像素长度,像素长度。

那么就请问了,一个超大屏幕的像素和你笔记本或者手机屏幕的像素大小相同吗?

也就是说1px在你手机屏幕上显示出来的长度可能为0.1mm,在露天演出的电子屏幕上长度为5cm,那么0.1mm和5cm相等吗?

感觉px好像是一个相对单位,但是如果放在网页或者设计人眼中,可能就不一定了。

上面举的那个例子是物理像素,在物理像素的背景下,px确实是一个相对单位。

但是在逻辑像素上就不同了,css中1px指的是逻辑像素,浏览器会将你的逻辑像素转化成物理像素。

每个设备之间虽然物理像素点大小不一样,但是用例逻辑像素的单位后,显示的长度就会一样了。

在开发网页的时候,写了10px,在你的设备上,逻辑1px为真实的1.2个像素大小,实际看上去为10cm。

没问题,换一个设备,逻辑1px为真实的2.4个像素大小。

也就是说另外一个设备像素大小是你的设备一半,那么对于他来说10px就是24个像素了,但是实际大小仍然为10cm,所以说,在有逻辑像素的概念的前提下,px是一个绝对长度单位。

总结如下:

  • 逻辑像素:CSS中的像素,绝对单位,保证不同设备下元素的尺寸是相同的。
  • 物理像素:设备屏幕实际拥有的像素点,相对单位,不同设备下物理像素大小不同。

通常移动端UI设计稿会按照iphone6的物理像素尺寸大小进行设计,即750px。

当然也可以按照逻辑像素进行设计,即375px,但是一般设计师不会这么干,主要为了设计稿更加清晰。

所以前端在量取尺寸的时候,需要除以2,才能适配页面中的CSS逻辑像素值。

好在现代UI工具如:蓝湖、PxCook等都具备自动除以2的标注信息方式。

这样当设计稿上的像素设置1px的时候,那么对应的逻辑像素就应该是0.5px,那么0.5px在新版浏览器中是支持的,所以我们这样写样式border:0.5px gray solid是没有任何问题的。

而在旧版浏览器中要做到兼容处理,就需要采用transform:scale(0.5)的方式了。

本文作者:西门老舅,web前端工程师,对前端CSS、JS、TS、Vue、React、小程序、工程化都有深入研究,乐于技术分享。