>

特性检测,中使用功能查询

- 编辑:至尊游戏网站 -

特性检测,中使用功能查询

在 CSS 中使用功能查询

2016/09/04 · CSS · 功能查询

原文出处: Jen Simmons   译文出处:Erichain_Zain   

在 CSS 里,有一个你可能没有听过的工具,但是它已经出现一段时间了,而且非常强大。也许,它会成为 CSS 中你最喜欢的东西之一。

那么,是什么呢?就是 @support,也就是功能查询。

通过 @support,你可以在 CSS 中使用一小段的测试来查看浏览器是否支持一个特定的 CSS 功能(这个功能可以是 CSS 的某种属性或者某个属性的某个值),然后,根据测试的结果来决定是否要应用某段样式。比如:

CSS

@supports ( display: grid ) { // 如果浏览器支持 Grid,这里面的代码才会执行 }

1
2
3
@supports ( display: grid ) {
    // 如果浏览器支持 Grid,这里面的代码才会执行
}

如果浏览器能够理解 display: grid,那么,大括号里的代码都会被应用,否则,这些样式就会被跳过。

现在,对于功能查询是什么,你也许还有一点疑惑。这并不是通过某种额外的验证来分析浏览器是否已经确切的实现了某个 CSS 属性。如果你需要查看额外的验证,可以查看 Test the Web Forward。

功能查询让浏览器自己就能够表现出是否支持某个 CSS 属性或者 CSS 属性值。然后通过这个结果来判断是否要应用某段 CSS。如果一个浏览器没有正确的(或者完全的)实现一个 CSS 属性,那么,@supports 就没有什么用了。它并不是一个能够让浏览器的 bug 消失的魔杖。

但是,我已经发现 @supports 是那么难以置信的有帮助。比起以前没有这个属性的时候,@supports 能够让我一再的使用新的 CSS 功能。

多年以来,开发者们都在使用 Modernizr 来实现功能查询,但是 Modernizr 需要 JavaScript。虽然这部分 JavaScript 很小,但是,CSS 结构中添加了 Modernizr 的话,在 CSS 被应用之前,就需要下载 JavaScript 然后等待执行完成。比起使用 CSS,加入了 JavaScript 总是会更慢。而且,要是 JavaScript 执行失败了呢?另外,Modernizr 还需要一层额外复杂的、很多项目都无法理解的东西。相比之下,功能查询更快,功能更强大,使用起来更简单。

你也许注意到了,@supports 的写法和媒体查询很类似,我觉得他们可能就是堂兄弟的关系。

CSS

@supports ( display: grid ) { main { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); } }

1
2
3
4
5
6
@supports ( display: grid ) {
    main {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    }
}

大多数时候,你其实不需要这样的测试。比如,你可以直接这样写:

CSS

aside { border: 1px solid black; border-radius: 1em; }

1
2
3
4
aside {
    border: 1px solid black;
    border-radius: 1em;
}

如果浏览器能够理解 border-radius,那么,在相应的容器上就会应用圆角样式。如果它不能理解这个属性,那么,就会直接跳过并继续执行下面的语句。容器的边缘也就保持直角了。完全没有必要使用功能查询或者测试,CSS 就是这样运作的。这是属于 CSS 中稳固设计,渐进增强的一个基本的原则。浏览器会直接跳过他们无法解析的语句,不会抛出任何错误。

图片 1

大多数浏览器都会应用 border-radius: 1em;,然后展示出右边的效果。但是,在 IE6,7,8 上你却不能看到圆角,你看到的将是左边的效果。可以看看这个例子:A Rounded Corner Box。

对于这个例子,没有必须要使用功能查询。

那么,什么时候才需要使用 @supports 呢?功能查询是将 CSS 声明绑定在一起的一个工具,以便于这些 CSS 规则能够在某种条件下以一个组合的方式运行。当你需要混合使用 CSS 的新规则和旧规则的时候,并且,仅当 CSS 新功能被支持的时候,就可以使用功能查询了。

译者注:以下例子中的 initial-letter 属性现在在所有现代浏览器中都不受支持,所以,以下例子中的代码可能都是无效的代码。如果下文中有提到此属性在某某浏览器中受支持的话,请忽略。需要了解 initial-letter 详细的说明,可以参考initial-letter | MDN。

来看一个关于使用 initial-letter 的例子。这个属性告诉浏览器要将特指的那个元素变得更大,就像一个段首大字一样。在这里,我们要做的就是让段落的第一个字母的大小为四行文字那么大。同时,我们再对它进行加粗,在它的右边设置一些 margin,还给它设置一个高亮的橘色。OK,很不错了。

CSS

p::first-letter { margin-right: 0.5em; color: #FE742F; font-weight: bold; -webkit-initial-letter: 4; initial-letter: 4; }

1
2
3
4
5
6
7
p::first-letter {
    margin-right: 0.5em;
    color: #FE742F;
    font-weight: bold;
    -webkit-initial-letter: 4;
    initial-letter: 4;
}

图片 2

这是在 Safari 上的效果

让我们看看在其他浏览器上的效果。

图片 3

好吧,简直没法接受。除了使用 initial-letter 来达到我们想要的效果之外,我们并不想要改变字体的 colormargin,和font-weight。所以,我们需要一个方法来测试浏览器是否支持 initial-letter 这个属性,然后在浏览器支持这个属性的时候再应用相关的样式。所以,使用功能查询:

CSS

@supports (initial-letter: 4) or (-webkit-initial-letter: 4) { p::first-letter { -webkit-initial-letter: 4; initial-letter: 4; color: #FE742F; font-weight: bold; margin-right: 0.5em; } }

1
2
3
4
5
6
7
8
9
@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {
    p::first-letter {
        -webkit-initial-letter: 4;
        initial-letter: 4;
        color: #FE742F;
        font-weight: bold;
        margin-right: 0.5em;
    }
}

注意,测试的时候需要进行完全的测试,CSS 属性和值都得写上。一开始我也比较疑惑,为什么非得测试 initial-letter: 4呢?4 这个值很重要吗?如果我写成 17 呢?莫非是需要匹配我即将要应用的 CSS 中的样式吗?

原因是这样的:@supports 在测试的时候,需要提供属性和值,因为有时候测试的是属性,有时候测试的是值。对于 initial-letter ,你输入多少值并不重要。但是,如果是 @suports ( dislay: grid ) 就不一样了,所有浏览器都识别 display,但是,并不是所有浏览器都识别 display: grid

回到我们的例子,当前的 initial-letter 只在 Safari 9 上受支持,并且需要加前缀。所以,我加了前缀,同时,保持着不加前缀的规则,并且,我还写了测试来测试另外的属性。没错,在功能查询中,还可以使用 and, or, not 声明。

下面是新的结果。理解 initial-letter 的浏览器会显示一个巨大加粗高亮的段首大字。其他浏览器的行为就像是这个段首大字不存在一样。当然,如果更多的浏览器支持了这个属性,那么,他们的行为也将会是有一个段首大字。

图片 4

深入探讨 CSS 特性检测 @supports 与 Modernizr

2017/03/01 · CSS · Modernizr

本文作者: 伯乐在线 - chokcoco 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

什么是 CSS 特性检测?我们知道,前端技术日新月异的今天,各种新技术新属性层出不穷。在 CSS 层面亦不例外。

一些新属性能极大提升用户体验以及减少工程师的工作量,并且在当下的前端氛围下:

  • 很多实验性功能未成为标准却被大量使用;
  • 需要兼容多终端,多浏览器,而各浏览器对某一新功能的实现表现的天差地别;

所以有了优雅降级和渐进增强的说法,在这种背景下,又想使用新的技术给用户提供更好的体验,又想做好回退机制保证低版本终端用户的基本体验,CSS 特性检测就应运而生了。

CSS 特性检测就是针对不同浏览器终端,判断当前浏览器对某个特性是否支持。运用 CSS 特性检测,我们可以在支持当前特性的浏览器环境下使用新的技术,而不支持的则做出某些回退机制。

本文将主要介绍两种 CSS 特性检测的方式:

  1. @supports
  2. modernizr

组织你的代码

现在,也许你迫不及待的想要使用这个工具来将你的代码分为两个分支,使其变得干净一些。“Hey,浏览器,如果你识别 Viewport 单位,就执行这个,否则,执行另外的”。感觉很不错,有条理。

CSS

@supports ( height: 100vh ) { // 支持 viewport height 的样式 } @supports not ( height: 100vh ) { // 对于旧浏览器的替代样式 } // 我们希望是好的,但这是一段烂代码

1
2
3
4
5
6
7
@supports ( height: 100vh ) {
    // 支持 viewport height 的样式
}
@supports not ( height: 100vh ) {
    // 对于旧浏览器的替代样式
}
// 我们希望是好的,但这是一段烂代码

这段代码并不好,至少当前看来是这样的。发现问题了吗?

问题就是,并不是所有的浏览器都支持功能查询,不理解 @supports 的浏览器会直接跳过两段代码,这也许就太糟糕了。

意思就是,除非浏览器百分之百支持功能查询,否则我们就没法使用了吗?当然不是,我们完全可以使用功能查询,而且应该使用功能查询,只要不像上面那样写代码就行。

那么,怎么做才对呢?其实与使用媒体查询类似,我们在浏览器完全支持媒体查询之前就开始使用了不是吗?事实上,功能查询使用起来比媒体查询更简单,只要脑子放聪明一点就行了。

你想要让你的代码知道浏览器是否支持功能查询或者正在测试的某个功能,我来告诉你怎么做。

当然,在未来,浏览器 100% 支持功能查询的时候,我们可以大量使用 @supports not 来组织我们的代码。

CSS @supports

传统的 CSS 特性检测都是通过 javascript 实现的,但是未来,原生 CSS 即可实现。

CSS @supports 通过 CSS 语法来实现特性检测,并在内部 CSS 区块中写入如果特性检测通过希望实现的 CSS 语句。

功能查询的支持情况

所以,@supports 现在支持度什么样了呢?

自从 2013 年中,@supports 就能够在 Firefox,Chrome 和 Opera 中使用了。在 Edge 的各个版本中也受支持。Safari 在 2015 年秋季才实现这个功能。具体的支持情况,请看下面这张图:

图片 5

你可能会觉得 IE 不支持此功能会是一个大问题,但是,其实不是这样的。待会儿就会告诉你原因。我相信,最大的一个障碍是 Safari 8,我们需要留意在这个浏览器上发生的事情。

让我们来看另外一个例子。假设我们有些布局代码,为了正常运行,需要使用 object-fit: cover;。对于不支持这个属性的浏览器,我们想要使用不同的样式。

图片 6

所以,我们可以这样写:

CSS

@supports (initial-letter: 4) or (-webkit-initial-letter: 4) { p::first-letter { -webkit-initial-letter: 4; initial-letter: 4; color: #FE742F; font-weight: bold; margin-right: 0.5em; } } div { width: 300px; background: yellow; } @supports (object-fit: cover) { img { object-fit: cover; } div { width: auto; background: green; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {
    p::first-letter {
        -webkit-initial-letter: 4;
        initial-letter: 4;
        color: #FE742F;
        font-weight: bold;
        margin-right: 0.5em;
    }
}
 
div {
    width: 300px;
    background: yellow;
}
@supports (object-fit: cover) {
    img {
        object-fit: cover;
    }
    div {
        width: auto;
        background: green;
    }
}

会发生什么呢?@supports 有支持或者不支持的情况,object-fit 也有支持或者不支持的情况,所以,就有了四种可能性:

功能查询支持情况 属性(或者值)支持情况 会发生什么 是否我们想要的
支持 不支持 CSS 将会被应用
支持 不支持 CSS 不会被应用
不支持 支持 CSS 不会被应用
不支持 不支持 CSS 不会被应用

语法:

CSS

@supports <supports_condition> { /* specific rules */ }

1
2
3
@supports <supports_condition> {
    /* specific rules */
}

举个例子:

Sass

{ position: fixed; } @supports (position:sticky) { div { position:sticky; } }

1
2
3
4
5
6
7
8
9
{
    position: fixed;
}
@supports (position:sticky) {
    div {
        position:sticky;
    }
}

上面的例子中,position: sticky 是 position 的一个新属性,用于实现黏性布局,可以轻松实现一些以往需要 javascript 才能实现的布局(戳我了解详情),但是目前只有在 -webkit- 内核下才得到支持。

上面的写法,首先定义了 div 的 position: fixed ,紧接着下面一句 @supports (position:sticky) 则是特性检测括号内的内容,如果当前浏览器支持 @supports 语法,并且支持 position:sticky 语法,那么 div 的 则会被设置为 position:sticky 。

我们可以看到,@supports 语法的核心就在于这一句:@supports (...) { } ,括号内是一个 CSS 表达式,如果浏览器判断括号内的表达式合法,那么接下来就会去渲染括号内的 CSS 表达式。除了这种最常规的用法,还可以配合其他几个关键字:

最佳实践

所以,我们应该怎么写功能查询的代码呢?像下面这样:

CSS

// fallback code for older browsers @supports ( display: grid ) { // code for newer browsers // including overrides of the code above, if needed }

1
2
3
4
5
6
// fallback code for older browsers
 
@supports ( display: grid ) {
    // code for newer browsers
    // including overrides of the code above, if needed
}

译者注:本文的主要内容是介绍功能查询和 @supports 的使用方法,所以,某些代码可能是无法运行的,希望读者们注意。同时,由于原文中的一些内容显得比较冗余,所以部分内容并没有翻译。如果需要了解详细内容,请查看原文。

1 赞 1 收藏 评论

图片 7

@supports not && @supports and && @supports or

@supports not — 非

not 操作符可以放在任何表达式的前面来产生一个新的表达式,新的表达式为原表达式的值的否定。看个例子:

Sass

@supports not (background: linear-gradient(90deg, red, yellow)) { div { background: red; } }

1
2
3
4
5
@supports not (background: linear-gradient(90deg, red, yellow)) {
    div {
        background: red;
    }
}

因为添加了 not 关键字,所以与上面第一个例子相反,这里如果检测到浏览器不支持线性渐变 background: linear-gradient(90deg, red, yellow) 的语法,则将 div 的颜色设置为红色 background: red 。

h3>@supports and — 与

这个也好理解,多重判断,类似 javascript 的 && 运算符符。用 and 操作符连接两个原始的表达式。只有两个原始表达式的值都为真,生成的表达式才为真,反之为假。

当然,and 可以连接任意多个表达式看个例子:

Sass

p { overflow: hidden; text-overflow: ellipsis; } @supports (display:-webkit-box) and (-webkit-line-clamp:2) and (-webkit-box-orient:vertical) { p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } }

1
2
3
4
5
6
7
8
9
10
11
p {
    overflow: hidden;
    text-overflow: ellipsis;
}
@supports (display:-webkit-box) and (-webkit-line-clamp:2) and (-webkit-box-orient:vertical) {
    p {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
    }
}

上面同时,检测 @supports (display:-webkit-box) and (-webkit-line-clamp:2) and (-webkit-box-orient:vertical) 了三个语法,如果同时支持,则设定三个 CSS 规则。这三个语法必须同时得到浏览器的支持,如果表达式为真,则可以用于实现多行省略效果:

Demo戳我

See the Pen @supportAnd by Chokcoco (@Chokcoco) on CodePen.

@supports or — 或

理解了 @supports and,就很好理解 @supports or 了,与 javascript 的 || 运算符类似,表达式中只要有一个为真,则生成表达式表达式为真。看例子:

Sass

@supports (background:-webkit-linear-gradient(0deg, yellow, red)) or (background:linear-gradient(90deg, yellow, red)){ div { background:-webkit-linear-gradient(0deg, yellow, red); background:linear-gradient(90deg, yellow, red) } }

1
2
3
4
5
6
@supports (background:-webkit-linear-gradient(0deg, yellow, red)) or (background:linear-gradient(90deg, yellow, red)){
    div {
        background:-webkit-linear-gradient(0deg, yellow, red);
        background:linear-gradient(90deg, yellow, red)
    }
}

上面的例子中,只有检测到浏览器支持 background:-webkit-linear-gradient(0deg, yellow, red) 或者(or) background:linear-gradient(90deg, yellow, red) 其中一个,则给 div 元素添加渐变。

Demo戳我

See the Pen @supports or by Chokcoco (@Chokcoco) on CodePen.

当然,关键字 not 还可以和 and 或者 or 混合使用。感兴趣的可以尝试一下。

Can i use?

兼容性来看,先看看 Can i use 吧:

图片 8

这仍是一个实验中的功能,虽然大部分浏览器都已经支持了,但是目前看来,即是在移动端想要全部兼容仍需要等待一段时间。

但是我们已经可以开始使用起来了,使用 @supports 实现渐进增强的效果。

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

CSS.supports()

谈到了 @supports,就有必要再说说 CSS.supports() 。

它是作为 @supports 的另一种形式出现的,我们可以使用 javascript 的方式来获得 CSS 属性的支持情况。

可以打开控制台,输入 CSS.supports 试试:

图片 9

如果没有自己实现 CSS.supports 这个方法,输出上述信息,表示浏览器是支持 @supports 语法的,使用如下:

JavaScript

CSS.supports('display', 'flex') // true CSS.supports('position', 'sticky') // true

1
2
CSS.supports('display', 'flex')  // true
CSS.supports('position', 'sticky')  // true

图片 10

那它有什么用呢?如果你的页面需要动态添加一些你不确定哪些浏览器支持的新的属性,那它也许会派上用场。以及,它可以配合我们下文即将要讲的 modernizr 。

modernizr

上面介绍了 CSS 方式的特性检测,在以前,通常是使用 javascript 来进行特性检测的,其中 modernizr 就是其中最为出色的佼佼者。

modernizr(戳我查看 Github )是一个开源的 javascript 库。有着将近 2W 的 star ,其优秀程度可见一斑。

简单看看使用方法,假设页面已经引用了 modernizr ,语法如下:

JavaScript

// Listen to a test, give it a callback Modernizr.on('testname', function( result ) { if (result) { console.log('The test passed!'); } else { console.log('The test failed!'); } }); // 或者是类似 CSS.supports() Modernizr.testAllProps('background', 'linear-gradient(90deg, #888, #ccc)'); // true

1
2
3
4
5
6
7
8
9
10
11
12
// Listen to a test, give it a callback
Modernizr.on('testname', function( result ) {
  if (result) {
    console.log('The test passed!');
  }
  else {
    console.log('The test failed!');
  }
});
// 或者是类似 CSS.supports()
Modernizr.testAllProps('background', 'linear-gradient(90deg, #888, #ccc)');  // true

举个实际的例子,假设我们希望对是否支持渐变这个样式浏览器下的一个 div 区别对待,有如下 CSS:

CSS

div { background: #aaa; } .linear-gradient div{ background: linear-gradient(90deg, #888, #ccc); }

1
2
3
4
5
6
7
div {
    background: #aaa;
}
.linear-gradient div{
    background: linear-gradient(90deg, #888, #ccc);
}

使用 Modernizr 进行判断,如果支持渐变,则在根元素添加一个 .linear-gradient 样式,方便示例,使用了 jquery 语法:

JavaScript

if (Modernizr.testAllProps('background', 'linear-gradient(90deg, #888, #ccc)')) { $('html').addClass('linear-gradient'); }

1
2
3
if (Modernizr.testAllProps('background', 'linear-gradient(90deg, #888, #ccc)')) {
    $('html').addClass('linear-gradient');
}

Demo戳我

See the Pen modernizr by Chokcoco (@Chokcoco) on CodePen.

当然,Modernizr 还有很多其他的功能,可以去翻翻它的 API 。

特性检测原理

如果嫌引入整一个 Modernizr 库太大,页面又不支持 @supports ,其实我们自己用简单的 javascript 实现也非常方便简单。

想要知道浏览器支持多少 CSS 属性,可以在调试窗口试试:

JavaScript

var root = document.documentElement; //HTML for(var key in root.style) { console.log(key); }

1
2
3
4
5
var root = document.documentElement; //HTML
for(var key in root.style) {
    console.log(key);
}

图片 11

上面图片截取的只是打印出来的一小部分。如果我们要检测某个属性样式是否被支持,在任意的 element.style 检测它是否存在即可,即上面代码示例的 root 可以替换成任意元素。

当然,元素可能有 background 属性,但是不支持具体的 linear-gradinet() 属性值。这个时候该如何检测呢?只需要将具体的值赋值给某一元素,再查询这个属性值能否被读取。

JavaScript

var root = document.documentElement; root.style.backgroundImage = 'linear-gradient(90deg, #888, #ccc)'; if(root.style.backgroundImage) { // 支持 } else { // 不支持 }

1
2
3
4
5
6
7
8
9
var root = document.documentElement;
root.style.backgroundImage = 'linear-gradient(90deg, #888, #ccc)';
if(root.style.backgroundImage) {
  // 支持
} else {
  // 不支持
}

所以上面 Modernizr 的例子里,javascript 代码可以改成:

JavaScript

var root = document.documentElement; root.style.backgroundImage = 'linear-gradient(90deg, #888, #ccc)'; if(root.style.backgroundImage) { $('html').addClass('linear-gradient'); }

1
2
3
4
5
6
var root = document.documentElement;
root.style.backgroundImage = 'linear-gradient(90deg, #888, #ccc)';
if(root.style.backgroundImage) {
  $('html').addClass('linear-gradient');
}

当然,做这种特定属性值判断的时候由于有个 CSS 赋值操作,所以我们选取用于判断的元素应该是一个隐藏在页面上的元素。

各种方式间的优劣

  • 原生的 @supports 的性能肯定是最好的,而且无需引入外部 javascript ,首推这个,但是无奈兼容问题,目前来看不是最好的选择。
  • Modernizr 功能强大,兼容性好,但是需要引入外部 javascript,多一个 http 请求,如果只是进行几个特性检测,有点杀鸡用牛刀的感觉。
  • 针对需要的特性检测,使用 javascript 实现一个简单的函数,再把上面用到的方法封装一下:

JavaScript

/** * 用于简单的 CSS 特性检测 * @param [String] property 需要检测的 CSS 属性名 * @param [String] value 样式的具体属性值 * @return [Boolean] 是否通过检查 */ function cssTest(property, value) { // 用于测试的元素,隐藏在页面上 var ele = document.getElementById('test-display-none'); // 只有一个参数的情况 if(arguments.length === 1) { if(property in ele.style) { return true; } // 两个参数的情况 }else if(arguments.length === 2){ ele.style[property] = value; if(ele.style[property]) { return true; } } return false; }

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
/**
* 用于简单的 CSS 特性检测
* @param [String] property 需要检测的 CSS 属性名
* @param [String] value 样式的具体属性值
* @return [Boolean] 是否通过检查
*/
function cssTest(property, value) {
    // 用于测试的元素,隐藏在页面上
    var ele = document.getElementById('test-display-none');
    // 只有一个参数的情况
    if(arguments.length === 1) {
        if(property in ele.style) {
            return true;
        }
    // 两个参数的情况
    }else if(arguments.length === 2){
        ele.style[property] = value;
        if(ele.style[property]) {
            return true;
        }
    }
    return false;
}

简单测试一下:

图片 12

软件工程没有银弹,所以无论哪种方式,都有适合的场景,我们要做的就是掌握了解它们的原理,根据不同的场景灵活运用即可。

系列 CSS 文章汇总在我的 Github 。

到此本文结束,如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

图片 13 图片 14

2 赞 收藏 评论

关于作者:chokcoco

图片 15

经不住流年似水,逃不过此间少年。 个人主页 · 我的文章 · 63 ·    

图片 16

本文由软件综合发布,转载请注明来源:特性检测,中使用功能查询