图解CSS:条件 CSS

在 CSS 的世界中,总是有很多实验性的属性先行,正因为这些先行者在不断的探索新的特性,才让 CSS 越来越强大。而这些实验性的特性并没有立马得到众多浏览器的支持,为了能让这些实验性特性能在部分支持的浏览器上运行,同时又能让不支持的浏览器做相应的降级处理。那么我们就会需要根据相关的条件进行判断。这也就是条件 CSS 的由来。

条件CSS的简介

事实上,条件 CSS 源于在多数浏览器上修正 CSS 渲染 Bug 的需求,以确保尽量多的用户看到正确的网站设计。核心思想是建立在 IE 条件注释方法,并扩展到包含其他的浏览器,而且将条件声明内联到 CSS 定义里面。

但随着技术不断的革新,条件 CSS 现在很少使用条件注释这样的方式来做条件判断,而是提供一些 CSS 特性(比如,CSS的@规则)及其相关的 JavaScript API 允许我们在满足特定条件时应用样式或行为。

需要注意的是,如果所有浏览器都能正确地执行 W3C 发布的 CSS 标准,那么条件 CSS 就没有需求了。但是,CSS在不同浏览器渲染总是会有或多或少的 Bug 存在,而且往往都及其让人沮丧。条件 CSS 给我们提供了一个简单的方法来解决这些问题。加上文章开头也提到过了,在 CSS 的社区中总是有很多先驱者在不断的探索和创造一些实验性 CSS 特性。我们也可以通过条件 CSS 在一些已得到的浏览器上先用起来。

条件CSS分类

到目前为止,条件 CSS 主要有三个@规则:

  • @media

  • @container

  • @supports

  • @viewport

其中@media@supports两个规则是我们常见的规则,@container 是近两年 CSS 新增的特性,而@viewport并不常见,它们都算得上是 条件 CSS 规则。

CSS中的 @ 规则

既然条件 CSS 运用到的也是 CSS 的 @ 规则,那么我们很有必要先简单的了解一下CSS 的@规则。

CSS的@规则(at-rule)是一条语句,它为CSS提供了执行或如何执行的指令。

@规则的每个语句都是以@开头,后面直接跟着相应的关键词,这些关键词充当CSS应该做什么的标识符。尽管每个@规则都有它的变体,但这也是最常见的语法规则。

CSS的@规则主要分为常规规则嵌套规则两大类。

常规规则

常规规则的语法较为简单,类似下面这样:

python 复制代码
@[关键词](规则)

常规规则常见的主要:

@charset

大家不知道有没有印象,早期在创建.scss.less文件时都会要求在第一行中使用@charset来声明字符集,比如:

CSS 复制代码
@charset 'utf-8'

在某些CSS属性(比如content)中使用非ASCII字符或样式表包含UTF-8等非ASCII字符时,@charset规则非常有用。另外,@charset规则必须是样式表中的第一个元素,并且前面不能有任何字符。用户代理必须忽略样式表开头之外的任何@charset规则。如果定义了几个@charset规则,则只使用第一个。

@import

@import允许用户从其他样式表导入样式规则。比如:

CSS 复制代码
@import url("https://fonts.googleapis.com/css?family=Libre+Baskerville");
@import url("print.css") print;
@import url("tv.css") projection, tv;
@import 'custom.css';
@import "common.css" screen, projection;
@import url('landscape.css') screen and (orientation:landscape);

导入样式规则时,就好像文件的内容就在规则所在的位置一样。这些规则必须先于所有其他类型的规则,@charset规则除外,否则@import规则会不生效。

在实际项目中不建议使用@import来引用其他CSS样式文件。这样做不但请求多,还会造成阻塞。

@namespace

@namespace规则对于很多同学而言会感到陌生。从词面上来了解,它是用来声明一个命名空间前缀,并将其与给定的命名空间关联起来。然后可以在命名空间限定的名称中使用此命名空间前缀。该规则对于将CSS应用到XHTML中特别有用,这样一来,XHTML元素就可以用作CSS中的选择器。可以使用定义的命名空间来限制泛型、类型和属性选择器,只选择该命名空间中的元素。@namespace规则通常只在处理包含多个命名空间的文档时有用,比如包含内联SVG或MathML的HTML,或者包含多个词汇表的XML。

CSS 复制代码
@namespace url(http://www.w3.org/1999/xhtml);
@namespace svg url(http://www.w3.org/2000/svg);

/* 和所有XHTML中的a元素匹配,因为XHTML是默认的命名空间 * /
a {
 color : red;
}

/* 和所有SVG中的a元素匹配 * /
svg| a {
 color : blue;
}

/* 同时匹配XHTML和SVG中的a元素 */
*|a {
    color: orange;
}

使用@namespace规则有几点要注意:

  • 任何@namespace规则都必须遵循所有@charset@import规则,并位于样式表中所有其他@规则和样式声明之前

  • @namespace规则可以用于定义样式表的默认命名空间。定义默认命名空间时,所有通用选择器和类型选择器(不包含属性选择器)仅应用于该命名空间中的元素

  • @namespace规则还可以用于定义命名空间的前缀。如果泛型、类型和属性选择器的前缀是命名空间的前缀,那么该选择器只在元素或属性的命名空间和名称匹配时才匹配

嵌套规则

嵌套规则和常规规则不同的是,在规则后面会带一个花括号{},括号中会嵌套一些样式规则:

CSS 复制代码
@[关键词] {
    /* 样式规则 */
}

CSS的嵌套规则主要有:

@font-face

CSS的@font-face规则允许我们引用自定义的字体,该规则消除了依赖于计算机上安装的有限字体数量的需求。在使用自定义定体时,需要先使用该规则来声明:

CSS 复制代码
@font-face {
    [ font-family: <family-name>; ] ||
    [ src: [ <url> [ format(<string>#) ]? | <font-face-name> ]#; ] ||
    [ unicode-range: <urange>#; ] ||
    [ font-variant: <font-variant>; ] ||
    [ font-feature-settings: normal | <feature-tag-value>#; ] ||
    [ font-stretch: <font-stretch>; ] ||
    [ font-weight: <weight>; ] ||
    [ font-style: <style>; ]
} 

每个@font-face规则为每个字体描述符(隐式或显式)指定一个值。规则中没有给出显式值的部分使用每个描述符列出的初始值。这些描述符仅适用于定义它们的@font-face规则的上下文中,而不适用于文档语言元素。没有关于描述符应用于哪些元素或这些值是否由子元素继承的概念。当给定的描述符在给定的@font-face规则中多次出现时,只使用最后一个描述符声明,并忽略该描述符所有先前声明。

另外,该规则允许选择与设计目标密切匹配的字体,而不是将字体选择限制为给定平台上可用的字体。一组字体描述符定义字体资源的位置,包括本地或外部的位置,以及单个外观的样式特征。多个@font-face规则可用于构造具有多种字体的字体族。使用CSS字体匹配规则,用户代理可以选择性地只下载所需的字体。

在使用@font-face时也有些细节需要注意:

  • 作用域的限制。Web字体受到作用域的限制,因此@font-face规则中引用的字体资源必须与使用它们的页面位于相同的作用域,除非使用HTTP访问控制来放宽这一限制

  • 不考虑指定文件的MIME类型,因为没有为TrueTypeOpenTypeWOFF字体定义MIME类型

  • @font-face不能在CSS选择器中声明

在项目中使用@font-face引用自定义定体涉及很多细节,有关于这方面的细节,请移步阅读《F-mods:可用于 @font-face 的新特性》一文。

@keyframes

@keyframes规则主要用来声明一个动画,在嵌套的规则中指定了动画各个节点(帧)的样式规则。

CSS 复制代码
@keyframes <keyframes-name> {
    <keyframe-block-list>
}

@keyframes只是声明了一个动画,如果没有被animation-name属性调用的话,那么该规则中的样式并不会起任何的作用。另外要使用关键帧列表有效,它必须包含动画开始和结束状态的规则,即0%from)和100%to)。如果没有指定这两个时间偏移量,那么@keyframes声明就会无效,解析器将会忽略它,并且不能用于animation中。

如果我们通过JavaScript来操作@keyframes中的规则,可以使用CSSOM中的CSSKeyframesRule

特别注意:在@keyframes规则中声明的样式规则会覆盖元素中的样式规则,哪怕是带有!important加强权重的样式规则。

有关于@keyframes更深入的介绍,请多步阅读《Web 动画之旅》中的《你所不知道的 @keyframes:剖析 CSS 帧动画》。

@media

@media规则是条件CSS中的一种,其条件是一个媒体查询。它由一个媒体查询列表(可以是空的)和一组规则组成。规则的条件是媒体查询的结果。

CSS 复制代码
@media <media-query-list> {
    <group-rule-body>
}

稍后会对 @media 展开介绍,如果你迫不急待,可以先移步阅读《CSS 媒体查询新特性:@media》!

@supports

@supports规则是条件CSS中的另一种,也是一条件组规则,其条件测试用户代理是否支持CSS属性/值对。它可以用于编写样式表,这些样式表在可用时使用新特性,但在不支持这些特性时将可以优雅地降级。

CSS 复制代码
@supports <supports-condition> {
    <group-rule-body>
}

稍后会对 @supports 展开介绍,如果你迫不急待,可以先移步阅读《CSS 判断:@supports》!

@viewport

@viewport规则事实上不是条件CSS中的一种。该规则在CSS中定义了一组嵌套的描述符,这些描述符主要用来控制移动设备上的viewport设置。

CSS 复制代码
@viewport {
    <group-rule-body>
}

比如:

CSS 复制代码
@viewport {
    min-width: 640px;
    max-width: 800px;
}

@page

@page规则主要用于打印文档时候修改一些CSS属性。使用@page我们只能改变部分CSS属性,例如间距属性margin, 打印相关的orphans, widows, 以及page-break-*, 其他CSS属性会被忽略。

CSS 复制代码
@page <page-selector-list> {
    <page-body>
}

@document

@document规则指定应用于特定页面的样式的条件。该规则可以指定一个或多个匹配函数,如果其中任何一个函数应用于URL,则该规则将对具有该URL的文档生效。比如说,这个CSS文件被子站A调用,和被子站C调用,我们可以通过域名匹配来执行不同的CSS样式。这样,我们可以有效避免冲突,或者防止外链之类。

CSS 复制代码
@document
    /* 页面URL需要是 */
    url(https://www.w3cplus.com/),
    
    /* 页面URL的开头必须是... */
    url-prefix(www.w3cplus.com/blog/),
    
    /* 该域上的所有页面 * /
domain(w3cplus.com),

    /* 所有https协议页面 * /
    regexp("https:.*"){
        /* 开始样式 */
        body {
            color: #444;
        }
    }

@font-feature-values

@font-feature-values规则主要用于给定字体家族的替代符号的索引定义命名值。它允许在font-variant-alternates中使用一个公共名称来替换OpenType中不可激活的特性,从而在使用多种字体时简化CSS。

CSS 复制代码
@font-feature-values <family-name># {
    <feature-value-block-list>
}

来看一个小示例:

CSS 复制代码
/* 在Font One中激活 cool-style 风格的字体 * /
@font-feature-values Font One {
 @styleset {
cool-style: 12 ;
}
}

/* 在Font Two中激活 cool-style 风格的字体 * /
@font-feature-values Font Two {
 @styleset {
cool-style: 4 ;
}
}

/* 与字体无关 */
.cool-look {
    font-variant-alternates: styleset(cool-style);
}

注意,font-variant-alternates 是 CSS 的 font-variant-* 属性中的一个,主要用于设置可变字体的样式。如果你对可变字体感兴趣,可以移步阅读《Web 上可变字体》!

@font-palette-values

@font-palette-values 规则定义了一个颜色调色板,并将该颜色调色板与特定字体关联起来。这使得 Web 开发者可以在彩色字体中选择任意颜色,而不仅仅是受限于字体文件中预先定义的调色板。此外,该规则与特定字体的关联使得调色板的名称在不同的字体中应用时可以产生不同的效果,从而在元素中使用多个字体时可以使用类似的颜色(即用于字体回退)。

@font-palette-values 规则表示字体中使用的颜色调色板。调色板由一组有序的颜色组成。使用 @font-palette-values 允许 Web 开发者引用存在于字体中的调色板,以及创建由作者定义的颜色填充的调色板。此外,它允许使用 Web 开发者描述的颜色来覆盖字体中调色板中的一组颜色。

调色板始终是完整的,这意味着无法描述一个存在丢失颜色的调色板。如果缺少颜色,它们将从由 base-palette 描述符标识的字体的调色板中获取。

@font-palette-values 规则的使用很简单,它由 @font-palette-values 后紧跟一描述符组成。它的语法如下:

CSS 复制代码
@font-palette-values <dashed-ident> {
    <declaration-list>
}

其中 <dashed-ident> 用来定义颜色调色板的名称,例如 --gray ;而 <declaration-list> 是一组描述符,它包括 font-familybase-paletteoverride-colors 三个属性。例如:

CSS 复制代码
@font-palette-values --cooler {
    font-family: Bixa;
    base-palette: 1;
    override-colors:
        1 #7EB7E4;
}

其中 base-paletteoverride-colors 不是必须的。

在使用CSS @font-palette-values 规则选择调色板后,你可以使用 font-palette 属性应用它:

CSS 复制代码
.cooler {
    font-family: Bixa;
    font-palette: --cooler;
}

有关于这方面更详细的介绍,可以移步阅读《Web 上彩色字体》!

注意,@font-feature-values@font-palette-values 是 CSS 中用于自定义体方面的两个新特性,它与 @font-face 相结合,允许 Web 开发者在 Web 上使用可变字体和彩色字体,而且还可以动化画它们,给文本添加动画效果。如果你对这方面知识感兴趣,建议你花点时间阅读下面这几篇文章:

@counter-style

@counter-style规则可以允许我们定义自定义的计数器的样式。计数器样式由@counter-style规则中的描述符来指定,主要由systemsymbolsadditive-symbolsnegativeprefiexsuffixrange等组成。该规则的一般形式是:

CSS 复制代码
@counter-style <counter-style-name> {
    [ system: <counter-system>; ] ||
    [ symbols: <counter-symbols>; ] ||
    [ additive-symbols: <additive-symbols>; ] ||
    [ negative: <negative-symbol>; ] ||
    [ prefix: <prefix>; ] ||
    [ suffix: <suffix>; ] ||
    [ range: <range>; ] ||
    [ pad: <padding>; ] ||
    [ speak-as: <speak-as>; ] ||
    [ fallback: <counter-style-name>; ]
}

具体使用的时候可以像下面这样:

CSS 复制代码
@counter-style circled-alpha {
    system: fixed;
    symbols: Ⓐ Ⓑ Ⓒ;
    suffix: " ";
}

li {
    list-style: circled-alpha;
}

有关于 @counter-style 更详细的介绍,可以移步阅读《CSS 计数器:@counter-style》!

@layer

CSS 中的 @layer 是 CSS 中的一个新特性。通过 @layer 规则,Web 开发者可以显式地将样式规则置于特定的图层中。这对于控制样式的加载和应用顺序、提高性能以及避免样式之间的不必要依赖非常有用。例如,可以将关键的基础样式规则置于较低的图层,而将特定于页面的样式规则放置在较高的图层,以确保首次渲染时页面更快地呈现。

使用示例:

CSS 复制代码
/* 放置在主图层(默认图层) */
body {
    font-family: 'Arial', sans-serif;
    color: #333;
}

/* 将这些规则放置在 'components' 图层中 */
@layer components {
    .button {
        background-color: #0084ff;
        color: #fff;
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
    }
    /* 其他组件规则... */
}

在这个例子中,@layer components.button 的样式规则置于一个名为 components 的图层中,使其能够更有序地加载和应用。

有关于 CSS 级联层 @layer 特性更详细的介绍,请移步阅读《现代 CSS》中的《CSS 分层:@layer 》课程!

@scope

@scope 规则允许你在特定的 DOM 子树中选择元素,精确地定位元素,而无需编写过于具体且难以覆盖的选择器,也无需将选择器过于紧密地与 DOM 结构耦合。

CSS 作用域 @scope 主要有两个卖点:基于接近度的样式为选择器设置下限。换句话说,作用域给 CSS 带来了两个关键的东西:

  • 一组样式可以根据在 DOM 中的接近程度覆盖另一组样式

  • 更多地控制选择器针对哪些元素(即更好地操作 CSS 的级联)

有关于 CSS 的 @scope 更详细的介绍,请移步阅读《CSS 的嵌套和作用域:& 和 @scope》!

@property

@property 是 CSS 的 @ 规则之一,但它隶属于 CSS Houdini,它是 CSS Houdini 中的一个基础 API。它允许 Web 开发者显式地自定义他们的 CSS 自定义属性,并且允许对自定义属性进行属性类型检查、设定默认值(初始值)以及定义该自定义属性是否可被继承。

CSS 复制代码
 @property --stop {
    syntax: "<percentage>" ; /* 指定值的语法类型是百分比 */ 
    initial-value: 50%; 
    inherits: false; 
} 

body { 
    --stop: 60%; 
    background-image: linear-gradient( to right, red, red var(--stop), gold var(--stop), gold ); 
}

CSS 的 @property 和原生自定义属性允许我们在开发中做很多有意义的事情,如果你对这方面感兴趣,建议你花点时间阅读下面这些文章:

@starting-style

@starting-style 是 CSS 新增的一个 @ 规则,该规则借助新的 Web 功能,用于实现对 display: none 的元素进行入场和出场的动画。该规则提供了一种在元素在页面上打开之前,浏览器可以查找的"打开之前(before-open)"样式的方式。这对于入场动画以及对弹出窗口或对话框等元素进行动画处理非常有用。它还可用于在创建元素并希望赋予其动画效果时使用。以下示例将 popover 属性以动画形式呈现,使其从视口外平滑进入视图并进入顶层。

CSS 复制代码
/*   IS-OPEN STATE   */
dialog[open] {
    translate: 0 0;
}

/*   EXIT STATE   */
dialog {
    transition: 
        translate 0.7s ease-out, 
        overlay 0.7s ease-out, 
        display 0.7s ease-out allow-discrete;
    translate: 0 100vh;
}

/*   0. BEFORE-OPEN STATE   */
@starting-style {
    dialog[open] {
        translate: 0 100vh;
    }
}

Demo 地址:codepen.io/web-dot-dev...

有关于 @starting-style 更详细的介绍,可以参阅《Web 动画之旅》的《帧动画与过渡动画:谁更适合你的业务场景》!

@container

容器查询允许你根据元素容器的大小、计算样式和状态来应用样式。其最大的特点是: 容器查询允许开发者定义任何一个元素为包含上下文,查询容器的后代元素可以根据查询容器的大小或计算样式、状态的变化来改变风格

换句话说,一个查询容器是通过使用容器类型属性(container-typecontainer)来指定其查询类型。同时,查询容器的后代元素的样式规则可以通过使用 @container 条件组规则进行独立设置。简单地说,查询容器(也被称为 CSS 包容)提供了一种方法来隔离页面的各个部分,并向浏览器声明这些部分在样式和布局方面与页面的其他部分是独立的

容器查询最早是只有尺寸查询,但随着时间的推移,容器查询新增了样式查询和状态查询。也就是说,容器查询包含三种类型:

  • 尺寸查询:根据查询容器大小来调整其后代元素的样式

  • 样式查询:根据查询容器样式或 CSS 变量来调整其后代元素的样式

  • 状态查询:根据查询容状态来调整其后代元素的样式

有关于这方面更详细的介绍,请移步阅读:

上面列出了CSS的@规则,其中@charset@import@font-face@keyframes@media@supports是我们常见或已在项目中有见过的@规则;而@namespace@viewport@page@document@font-feature-values@counter-style等规则是我们不怎么常见。

在众多CSS的@规则中,@media@supports@viewport 以及新增的 @container 又被称为是条件CSS。

条件CSS之 @media

就前端开发者而言都避免不了面对众多设备终端的适配。而设备终端可谓是绫罗满目,用下图来形容一点不为过:

面对这样的场景,早在2010年社区就提出了响应式设计(Responsive Design)的概念。

响应式设计指的是,你的Web应用程序或Web页面应该从宽屏显示器到手机终端屏幕的所有东西上都显示得一样的好。

这是一种Web设计和开发的方法,他的最初目的就是在可限的空间最好方式展示最全、最优内容(布局)。它削除了网站在移动端和桌面端之间的差别。换句话说,用户在拖拉浏览器改变视口时能以最佳的方式实现Web应用程序或Web页面的布局。

而在响应式设计中,最为关键的就是条件CSS中的媒体查询 ,即@media媒体查询可以有条件的应用CSS规则,它告诉浏览器应该忽略或应用哪些CSS规则,而这些都取决于用户的设备终端。

媒体查询让我们将相同的HTML内容在不同的设备终端运用不同的CSS规则,最终向用户呈现不同的布局效果。因此,与其为智能手机维护一个网站,为笔记本电脑或台式机维护另一个网站,还不如借助媒体查询的特性来为不同的终端设备维护相同的HTML结构,然后再利用不同的规则展示不同的布局风格。这从维护成本上来说,还是有利的。

既然媒体查询这么强大,那么我们应该怎么来使用媒体查询呢?或者说媒体查询包含了哪些知识呢?这也是我们接下来要了解和学习的东西。

媒体查询是什么?

媒体查询是一种条件CSS,它提供了一种规则,让我们在符合条件的情况之下调用正确的CSS规则。而这个条件可以根据使用的设备类型视口大小屏幕像素密度 甚至设备方向

简单地说,媒体查询使用@media规则,后面跟着一个媒体类型 、零个或多个媒体特性 ,或者一个或多个媒体类型 和一个或多个媒体特性。然后再符合条件的特定范围输出正确的CSS规则。

媒体查询语法

媒体查询包含一个可选的媒体类型媒体特性 表达式(零个或多个)最终会被解析为true(符合条件规则)或false(不符合条件规则)。如果媒体查询中指定的媒体类型匹配展示文档所使用的设备类型,并且所有的表达式的值都是true,那么该媒体查询的结果为true

先忽略此图的具体意思,随着后续内容的完善,我们都能看懂此图的含义。

在实际使用@media规则时,不管是使用哪种方式,都是可以使用的,比如:

HTML 复制代码
<!-- link元素中的CSS媒体查询 -->
<link rel="stylesheet" media="screen and (max-width: 800px)" href="example.css" />
CSS 复制代码
<!-- import导入CSS中的媒体查询 -->
@import url(example.css) screen and (color), projection and (color);
HTML 复制代码
<!-- 样式表中的CSS媒体查询 -->
<style>
    @media screen and (max-width: 600px) {
        :root {
            color: green;
        }
    }
</style>

当媒体查询中的条件规则为true时,其对应的样式表或样式规则就会遵循正常的级联规则进行应用。即使媒体查询的规则返回的是false<link>标签和@import指向的样式规则也将会被下载,但是它们不会被应用到页面上。

针对媒体查询的语法规则,我们可以用一张细化的图来描述:

从上图可以看出,整个媒体查询规则中主要包含了三个部分:媒体类型媒体特性逻辑操作符。接下来,我们主要围绕着这三个方面进行展开。

媒体查询类型

媒体查询类型简称为媒体类型 ,它是媒体查询条件中的重要部分之一。媒体类型允许你在不同的媒体类型上指定相应的样式文件。原始媒体类型集是在HTML4中定义的,主要用于<link>元素上的媒体属性,比如screenprint等。比如下面的代码,大家应该不会感到陌生:

HTML 复制代码
<link href="style.css" media="screen" />
<link href="print.css" media="print" />

而事实上,除了我们常见的allscreenprint媒体类型之外,还有其他的一些媒体类型:

不幸的是,媒体类型作为区分具有不同样式需求的设备的一种方式已经是不够的。一些原本非常不同的类别,比如屏幕(screen)和手持设备(handheld)已经显著地融合在一起。其他类型,比如ttytv,暴露了与全功能计算机显示器的标准的有用差异,因此对使用不同样式的目标有用,但是媒体类型的互斥定义使它们难以以合理的方式使用;相反,它们独有的方面可以用媒体特性来处理。

对于媒体类型,我们常见的使用方式主要有:

HTML 复制代码
<link href="style.css" media="screen print" />
CSS 复制代码
@import url("style.css") screen;
HTML 复制代码
<style media="screen">
    @import url("style.css");
</style>
CSS 复制代码
@media screen{
    selector{rules}
}

媒体特性

刚才也提到过了,目前就媒体类型(设备终端)有一定的缺陷,因此为了更好的使用媒体查询规则,还会借助媒体特性来加强条件规则方面的判断。比如下表所列:

上面这些是我们常见的一些媒体特性,但还有一些我们不常见的媒体特性,比如:

上面两个表格中是 Media Queries Level 4 所列出的媒体特性,有常用的,也有少见的,但具体何时使用什么样的媒体查询特性来增强@media规则条件,需要根据具体的需求来判断。稍后我们会举一些简单的示例,来增强大家对媒体特性的理解。

除此之外, Media Queries Level 5 草案在媒体特性方面还进行了增强,纳入到Level 5的媒体查询特性主要有:

特别声明,Level 5中提到的媒体特性都和系统设置有所关系,另外这些特性还处于草案阶段,随时都有可能会更改或删除。这里所列仅供参考。

就上面所列的几个表格,大家都会觉得媒体特性类型众多,为了能更好的帮助大家理解相关的使用,接下来以常用的媒体特性为例,给大家列几个示例。

不知道大家有没有发现,在上面的几个表格中,有一个选项 是否接受 min max 的前缀 。因为在媒体特性中,大多数的媒体特性都可以带有minmax的前缀,比如说min-widthmax-width,用于表达 "最小的..." 或者 最大的... 。用两张图来帮助大家来理解minmax的实际含义。

使用 min max 前缀是主要为了避免与HTML或XML中的 < 或字符相冲突。如果你觉得使用 < 或符更易于理解的话,那么可以使用postcss-media-minmax插件来帮助你。

有了这些基础,我们来看看示例。

如果你想向最小宽度的20em的手持设备或屏幕应用样式表,你可以使用下面这样的媒体查询规则:

CSS 复制代码
@media handheld and (min-width: 20em), screen and (min-width: 20em) {
    /* 样式规则 */
}

如果你想向宽度在20em36em之间的屏幕运用不同的样式规则,则可以使用下面这样的媒体查询规则:

CSS 复制代码
@media screen and (min-width: 20em) and (max-width: 36em) {
    /* 样式规则 */
}

如果你想向最小宽度为375px和最小高度为812px的屏幕上运用相应的样式规则,则可以像下面这样写:

CSS 复制代码
@media only screen and (min-width: 375px) and (min-height: 812px) {
    /* 样式规则 /
} 

另外你要是想通过横屏或竖屏来区分,则可以像下面这样:

CSS 复制代码
 @media  only screen and ( min-width : 812px ) and ( orientation : landscape) {
   /* 横屏样式规则 * /
}

@media  only screen and ( min-width : 375px ) and ( orientation : portrait) {
   /* 竖屏样式规则 * /
} 

甚至还可以根据屏幕分辨率来写:

CSS 复制代码
 @media
 only screen and ( min-device-pixel-ratio : 3 ),
 only screen and ( min-resolution : 384dpi ),
 only screen and ( min-resolution : 3dppx ) {
       /* Retina屏幕下的样式规则 */
}

上面看到的仅仅是其中的一部分,也是我们常见的一些。对于不常见的媒体特性的规则,这里只向大家展示Level 5中的prefers-reduced-motion特性。为什么要特意介绍这个特性呢?主要是因为这个特性特别有意思。因为该特性可能通过特性检测区分并对一些配置较差或主动开启系统减弱动态效果的用户进行体验优化。

减弱动态效果设置是系统的一个设置,无论是在MacOS还是iOS时都隐藏的比较深。

对于MacOS系统,可以根据下面这个操作路径进行操作:

进入 系统偏好设置 => 辅助功能 => 显示器

开启 减弱动态效果,如下图所示:

对于iOS系统,可以根据下面这个操作路径进行操作:

进入 设置 => 通用 => 辅助功能

开启 减弱动态效果, 如下图所示:

开启「减弱动态效果」可以有效地降低 MacOS/iOS 系统糟糕的晕眩效果性能开销,从而达到系统更流畅的功效。具体使用的时候,就可以借助媒体特性的规则来处理:

CSS 复制代码
@keyframes aniName {
    /* 声明动画,在@规则中有介绍过 */
}

.background {
 animation : aniName 10s infinite alternate;
}

/* 开启 减弱动态效果 的设备会禁用 aniName动画 */
@media screen and (prefers-reduced-motion) {
    /* 禁用不必要的动画 */
    .element {
        animation: none;
    }
}

如何借助CSS的自定义属性,可以让上面的代码变得更为简单:

CSS 复制代码
:root {
    --anim-duration: 10s
}

@media screen and (prefers-reduced-motion) {
    :root {
        --anim-duration: 0s;
    }
}

.element {
    animation: aniName var(--anim-duration) infinite alternate;
}

这个功能非常适合为低配置设备的用户以及追求性能的用户做体验优化,因为很多用户会开启 减弱动态效果 来在旧设备上提升系统流畅度。

逻辑操作符

从上面示例代码我们不难发现,在@media规则中有出现过and这样的关键词。而这个关键词在媒体查询中被称为逻辑操作符 。在@media可用的逻辑操作符,除了and之外,还有notonly等,这些逻辑操作符构建复杂的媒体查询规则。这几个逻辑操作符有点类似于JavaScript中的。其中:

  • and操作符用来把多个媒体规则组合成一条媒体查询规则,对成链式的特征进行请求,只有当每个规则都为真时,结果才为真(有点类似于的概念)。

  • not操作符用来对一条媒体查询规则的结果进行取反(有点类似于的概念)

  • only操作符仅在媒体查询规则匹配成功的情况下被应用于一个样式,这对于防止让选中的样式在老式浏览器中被应用到

若使用了 not only 操作符,必须明确指定一个媒体类型!

另外,可以将多个媒体查询规则以逗号( , 分隔放在一起,只要其中任何一个为 ,整个媒体查询规则语句返回就是 。相当于 or 操作符。

接下来,我们分别来看看每个操作符具体使用的细节。

and

and逻辑符主要是让你将多个媒体查询规则(多个媒体属性或媒体属性与媒体类型)合并在一起。一个基本的媒体查询规则,即一个媒体属性与默认指定的 all 媒体类型,就像下面这样子:

CSS 复制代码
@media (min-width: 20em) {
    :root {
        --font-size: 100%;
    }
}

如果你只想在横屏时应用这个规则,你可以使用and逻辑符,加上orientation特性,比如:

CSS 复制代码
@media (min-width: 20em) and (orientation: landscape) {
    :root {
        --font-size: 100%;
    }
}

上面的规则是查询仅在可视区域宽度不小于20em并在横屏的设备下有效。如果,你仅想在电视媒体上应用,那么可以继续使用and逻辑符来合并媒体类型:

CSS 复制代码
@media tv and (min-width: 20em) and (orientation: landscape) {
    :root {
        --font-size: 100%;
    }
}

not

not逻辑符应用于整个媒体查询规则,在媒体查询规则为 时返回 。比如monochrome就用于彩色显示设备上或一个600px的屏幕应用于min-width: 700px属性查询上。在逗号媒体查询列表中not仅会否定它应用到它应用到的媒体查询上而不影响其它的媒体查询。not逻辑符仅能应用于整个媒体查询规则,而不能单独地应用于一个独立的媒体查询规则。例如,not在下面的媒体查询中最后被计算:

CSS 复制代码
@media not all and (monochrome) {
    /* 样式规则 * /
} 

上面的规则等价于:

CSS 复制代码
 @media  not (all and ( monochrome )) {
    /* 样式规则 * /
} 

而不是:

CSS 复制代码
 @media ( not all) and ( monochrome ) {
    /* 样式规则 * /
} 

再来看一个示例:

CSS 复制代码
 @media  not screen and ( color ), print and ( color ) {
    /* 样式规则 * /
} 

等价于:

CSS 复制代码
 @media ( not (screen and ( color ))), print and ( color ) {
    /* 样式规则 */
}

or 和 逗号分隔符

or逻辑操作符相当于JavaScript逻辑运符中的 ,即,当你的媒体查询规则中有多个的时候,只要有一个规则符合条件,其结果就是true。另外,在媒查询中,还有一个特殊规则,那就是逗号( , 分隔符,其效果等同于or逻辑操作符。当使用逗号分隔的媒体查询规则时,如果任何一个媒体查询规则返回的是真,那对应的样式规则就会生效。逗号分隔的列表中每个媒体查询规则都是独立的,一个媒体查询规则中的操作符并不会影响其它的媒体查询规则。也就是说,逗号分隔的媒体查询规则列表能够作用于不同的媒体属性、类型和状态

例如,如果你想在最小宽度为760px或横屏的手持设备上应用一组样式,可以这样写:

CSS 复制代码
@media (min-width: 760px), handheld and (orientation: landscape) {
    /* 样式规则 */
}

正如上面代码所示,如果一个800px宽度的屏幕设备,将返回真,逗号前一部分规则相当于@media all and (min-width: 760px)将会应用于该设备并且返回真,尽管我的屏幕媒体类型并不与第二部分(逗号后面一部分规则)的手持媒体类型相符合。同样的,如果我是一个500px宽的横屏手持设备,尽管第一部分因为宽度并不匹配,但第二部分仍匹配,因此整个媒体查询也会返回值。

only

only逻辑操作符有点特殊,它隐藏了旧浏览器的整个查询(防止老旧的浏览器不支持带媒体属性的查询而应用到给定的样式)。换句话说,旧的浏览器不理解only逻辑操作符,因此会忽略整个媒体查询规则。否则只会无效:

CSS 复制代码
@media only all and (min-width: 320px) and (max-width: 480px) {
    /* 忽略老的浏览器样式规则 */
}

not逻辑操作符类似,only逻辑操作符对于使用媒体类型是不可选的。

不支持媒体查询Level 3规则的浏览器已经非常少见了,所以在大多数情况之下,使用only逻辑操作符是不必要的。

但是 Media Queries Level 4 规范引入了一种新的语法,它使用常见的数学比较运算符(如 <>= )来确定视窗宽度的范围,这在语法上更有意义,同时编写的代码更少,更易于理解代码。

上图中使用 @media 语法表达的话,像下面这样:

CSS 复制代码
/* 老方式的查询范围语法 * / 
@media ( min-width : 375px ) {
    /* 视窗宽度大于或等于 375px */ 
}

@media ( max-width : 768px ) {
    /* 视窗宽度小于或等于 768px */
}

@media ( min-width : 375px ) and ( max-width : 768px ) {
    /* 视窗宽度在 375px ~ 768px 之间 */ 
}

/* 新式的查询范围语法 */ 
@media (width >= 375px) {
    /* 视窗宽度大于或等于 375px */ 
}

@media (width <= 768px) { 
    /* 视窗宽度小于或等于 768px */
}  

@media (375px <= width <= 768px) { 
    /* 视窗宽度在 375px ~ 768px 之间 */ 
} 

Media Queries Level 4 规范中最大的变化是我们有了比较值而不是组合值的新操作符:

  • < :计算一个值是否小于另一个值

  • > :计算一个值是否大于另一个值

  • = :计算一个值是否等于另一个值

  • >= :计算一个值是否大于或等于另一个值

  • <= :计算一个值是否小于或等于另一个值

媒体查询API

在DOM中有一个特性,可以通过JavaScript来获取媒体查询的结果。可以使用MediaQueryList接口和它的方法来实现。一旦创建了MediaQueryList对象,咱们就可以通过它来检查查询结果,或者也可以设置一些属性,来实现当查询结果变化时,自动接收到通知。

创建媒体查询列表

在获取媒体查询结果之前,首先要创建MediaQueryList对象,用来存储媒体查询。为了实现这个目的,需要使用 window.matchMedia() 方法。

举个例子,比如你想设置一个查询列表用来判定设备屏幕处于横屏还是竖屏,那你可以像下面这样编码:

JavaScript 复制代码
window.matchMedia("(orientation: portrait)");

当设备屏幕是横屏时,MediaQueryList对象中的matches属性返回的值为false,如下图所示:

反之,当你的设备是竖屏时,MediaQueryList对象中的matches属性返回的值为true,如下图所示:

从上面的示例也可以看出来,matchMedia()方法的使用很简单,只需要给这个方法传一个mediaQueryString参数,该参数是一个字符串,表示即将返回一个新MediaQueryList对象的媒体查询。返回来的MediaQueryList对象包含两个属性:

  • media,是一个DOMString类型,返回一个序列化的媒体查询列表

  • matches,返回的是一个布尔值,匹配则为true,否则为false

比如下面这个示例:

JavaScript 复制代码
window.matchMedia("(min-width: 400px)")
// => MediaQueryList {media: "(min-width: 400px)", matches: true, onchange: null}
window.matchMedia("(min-width: 400px)").media
// => "(min-width: 400px)"
window.matchMedia("(min-width: 400px)").matches
// => true

那么在一些情况之下,就可以借助新返回的MediaQueryList对象的matches属性做一些事情,比如:

JavaScript 复制代码
if (window.matchMedia("(min-width: 400px)").matches) {
    // 如果视窗宽度大于或等于400px,返回的值为true
    // 可以针对符合条件的情况下做一些想做的事情
} else {
    // 视窗小于400px的情况下,做一些自己想做的事情
}

接收和终止媒体查询的通知

如果想要接收媒体查询的提醒,我们就需要注册一个监听器来帮助我们,这样做要比手动查询更为有效。可以在MediaQueryList对象上使用addListener()方法,这样就通过实现MediaQueryListListener接口来指定一个监听器。比如下面这个示例:

JavaScript 复制代码
let receiveMediaQueryMessage = window.matchMedia('(orientation: portrait)')
receiveMediaQueryMessage.addListener(handleOrientationChange)

function handleOrientationChange (receiveMediaQueryMessage) {
    if (receiveMediaQueryMessage.matches) {
        console.log('现在处在竖屏')
    } else {
        console.log('现在处在横屏')
    }
}
handleOrientationChange(receiveMediaQueryMessage)

当你的手持设备不断的在横屏与竖屏之间切换时,console.log打印出来的值也会随之变化,如下图所示:

上面的示例,咱们通过window.matchMedia()方法创建了一个屏幕方向检测的查询列表receiveMediaQueryMessage,并且添加了一个事件监听。需要注意的是,当我们添加监听之后,我们其实直接调用了一次监听。这会让我们的监听器以目前设备的屏幕方向来初始化判定代码。也就是说,如果我们代码中设定的设备处理竖屏模式,而实际上它在启动时处理横屏模式,那么我们在后面的判定就会出现矛盾。然后我们可以在handleOrientationChange()函数中来查看媒体查询结果,并且可以设置屏幕方向变化后的逻辑处理代码。

上面示例演示的是,使用addListener()来接收媒体查询的通知,如果你不想再要接收媒体查询值变化的相关通知时,可以在MediaQueryList上调用removeListener()方法来移除监听,比如:

JavaScript 复制代码
receiveMediaQueryMessage.removeListener(handleOrientationChange)

使用CSS媒体查询常的缺点

CSS的@media规则(媒体查询)是为某些东西设计的,虽然很多人都说媒体查询是响应式Web设计的基石,但事实上那不是响应式Web设计。听起来是不是有些矛盾,那么根据我的经验,我来说说CSS媒体查询时常碰到的问题。

不直观

直到现在为止还没有哪位Web开发者说CSS媒体查询是直观的。虽然定义媒体查询规则非常简单,但是并不总是非常清楚,媒体查询规则在真实的浏览器,真实的设备和无数的场景中情况又是如何?

比如:

CSS 复制代码
@media only screen and (min-width: 320px) and (max-width: 480px) {
    /* 样式规则 */
}

上面的媒体查询规则的意思是:当浏览器视窗的宽度是 320px 480px 之间时符合媒体查询规则,返回的值是 true ,就会调用对应的样式规则。事实上,当你想做一些更具体的事情时,比如设备是平板电脑的横屏操作中,应用一个样式规则,这并不完全是确定的或直观的。设置一个媒体查询来做到这一点并不是不可能,但它绝对不是直观的。

限制条件

CSS媒体查询是动态的,允许你在CSS中定义条件语句。例如,如果视口位于这个和那个之间,那么执行另一段样式规则。然而,你这只是考虑了视口方面的限制,但事实上许多Web设计中还会考虑使用的场景。比如说,移动端的TabBar,对于不同的系统场景,他们的布局是有所不同的。例如,iOS设备,系统TabBar常常通栏位于屏幕底部,而Android设备却刚好相反,位于屏幕顶部。

那么问题来了,CSS媒体查询规则又怎么才能根据系统对某些UI元素设置不同的样式呢?

就算是你绕着走,可以使用CSS媒体查询来做到这一点,但也不能这么做,因为CSS媒体查询不是用于任何特性构建的。除此之外,你可能还需要通过CSS进行许多其他定制,但是当你需要不同程度的简单到高级条件时,媒体查询就不是一个很好的解决方案了。

不是本地扩展

CSS媒体查询是嵌入在浏览器中的一个功能特性。这也意味着它不是本机可扩展的。也就是说,不能通过CSS接口为本机添加额外的CSS媒体查询规或增加其功能。即使新的CSS媒体查询特性得到了Web标准流程的认可,要达到普实性,也还需要较长的一段时间。此外,并不是所有添加的特性都对你有用,因此,如果你没有得到你想要的东西,那么你需要找到其他方法来解决你的问题。

当然,有一种方法可以扩展CSS,但是你必须对JavaScript相关的知识要有深入的了解,而对于大多数Web开发人员来说,这不是一个实用的过程。

开发效率低

使用媒体查询为不同的场景实现不同的Web效果(响应式Web设计),有可能造成你的代码量成倍增加,因为你要为不同的断点添加单独的样式规则。比如:

CSS 复制代码
:root {
    --font-size: 100%;
}

body {
    font-size: var(--font-size);
}

/* 竖屏 * /
@media  only screen
 and ( min-device-width : 320px )
 and ( max-device-width : 480px )
 and ( -webkit-min-device-pixel-ratio : 2 )
 and ( orientation : portrait) {
 :root {
 font-size : 80% ;
}
}
/* 横屏 */
@media only screen
    and (min-device-width: 320px)
    and (max-device-width: 480px)
    and (-webkit-min-device-pixel-ratio: 2)
    and (orientation: landscape) {
    :root {
        --font-size: 120%;
    }
}

除了代码量的增加之外,还会增加工作流程的复杂性。为什么这么说呢?

了解响应Web设计的同学都应该知道,CSS媒体查询大部分在处理布局上的调整。因此,要做更多的事情,甚至还有的时候需要借助JavaScript来弥补其不足之处。同时为你后期的测试也增加了倍数量的工作。

响应Web性能

由于CSS媒体查询的工作方式,你最终需要更多的CSS代码,才能满足你的Web设计需求。根据HTTPArchive.org过去对响应式Web设计的一个数据统计可以得知,CSS文件的大小在过去五年中增加了114%,HTML文件大小的增长在同一时期达到了53%的峰值。

这种特殊的情况对您的网站的性能是会有一定的影响,因为在实现CSS媒体查询之后,它的速度肯定比以前慢,特别是对于使用不太理想的移动宽带网络的移动设备。而且,除了文件大小增加的问题之外,CSS媒体查询中没有任何内部机制可以真正提高Web页面的性能。

既然CSS媒体查询存在这么多的不足,那么为什么还有很多Web开发人员使用该技术来实现响应式的Web设计呢?其实这也是有一定的历史原因的。

最初的CSS媒体查询是用来辅助你在Web浏览器中实现一些特殊的效果,比如说,在高屏和短屏的设备中做一些元素上距离调整等。但后来,CSS媒体查询被社区用来承担整个响应式Web设计的重任。这好比,你只能吃一碗饭,然后店家非得让让吃十确碗饭,你说冤屈不。

因此,在一些Web的特殊场景,CSS媒体查询规则,还是具有一定的特殊能力,能帮助众多Web开发人员解决一些痛点。这也是CSS媒体查询规则最初设计的初衷。因此大家不要迷信CSS媒体查询规则是万能的,它只是在最适合的场景使用才是最佳的。

注意,CSS 媒体查询是一个庞大的知识体系:

如果你想更进一步了解这个体系,建议你花时间阅读下面这几篇文章:

条件 CSS 之 @container

CSS 的 @container 规则是 CSS 容器查询中的一个规则,容器查询中的尺寸查询规则与 @media 非常相似:

CSS 复制代码
/* 尺寸查询 */
@container card (width >= 30ch) {
    /* 样式规则 */
}

@media 不同的是,它只查询仅定义包含性上下文的父容器,即在父容器中显式使用 container-type 或它的简写属性 container 指定该容器为包含性上下文:

CSS 复制代码
.parent {
    container-type: inline-size;
}

@container card (width >= 30ch) {
    .childer {
        /* 样式规则 */
    }
}

虽然容器查询不能像媒体查询那样查询设备相关信息,但它也可以查询父容器样式或状态:

CSS 复制代码
/* 样式查询 */
@container style(border-color: lightblue) {
    /* 样式规则 */
}

@container state(dir: rtl) {
    /* 样式规则 */
}

需要知道的是,容器查询的出现并不是用来替代媒体查询的,它们两者应该是共存的关系。容器查询特性的出现,我们可以不再局限于视窗断点来调整布局或 UI 样式,还可以基于容器断点来调整布局或 UI 。换句话说,媒体查询是一种宏观的布局(Macro Layout),可以用于整体页面布局;而容器查询可以调整组件的每个元素,创建了一种微观的布局(Micro Layout)

如果你对这方面知识感兴趣,建议你移步阅读下面相关教程:

条件CSS之 @supports

Web开发者都知道,不同的浏览器(不管是现代浏览器还是老版本的IE浏览器)对Web页面的解析都会有所不一样,为了让Web页面在这些浏览器下渲染达到基本一致的效果,给用户更好的体验,开发者有时候需要为他们提供不同的样式代码。

而早期的时候,大多都是依赖于JavaScript来检测,然后提供不同的样式规则,很多时候为了节省时间和成本,会直接使用Modernizr这样的第三方JavaScript库来完成。但这样做真的有用吗?除了要懂怎么检测之外,还需要了解更多的有关于浏览器渲染机制,这样一来对于很多开发人员而言是较为痛苦的。

不过,幸运的是,条件CSS除了@meida之外,还提供了另一个条件CSS规则,即 @supports 规则,它允许我们可以根据浏览器对CSS特性的支持情况来定义不同的样式。这对于我们来说是非常重要的。

@supports 规则作用是什么

用一句话来说,@supports规则的作用是:

用来查询浏览器是否支持CSS的特性!

'前面也提到过,前端社区有很多大神一直在致力于CSS技术的革新和新特性的推进。所以每一年都会有不少的新特性或CSS的实验性特性的出现,但由于各大浏览器厂商对于这些新特性的支持度是有所差异,甚至支持的友好度也是不一致的。换句话说,或许你知道新出现的CSS特性,但又担心浏览器不支持,从而又不敢使用。那么这个时候就可以借助于@supports特性来完成对浏览器的检测。从而可以放心的在支持的浏览器中使用这些新特性CSS,对于不支持的浏览器可以轻易的提供一些降级处理。让你的作品在用户面前展示最佳的效果。

@supports 规则

@supports规则和@media规则有点类似。在@规则(@supports)后面会紧跟一个或多个条件语句<supports-condition>,再紧跟着{},在{}中嵌套着你想要的CSS规则。如果条件符合,即返回的值是true,浏览器就会运用{}中的CSS样式规则;反之,条件不符合,返回的值是false,浏览器即会忽略{}中的样式规则。用一张图来描述,大家会更易于理解:

@supports中的条件规则是由一个或多个由不同的逻辑操作符组成的表达式声明组合而成的。使用小括号()可以调整这些表达式之间的运算优先级。而其中的条件表达式就CSS声明 ,也就是一个CSS属性后跟一个属性值,比如property:value。比如说,display: flex这样的一个表达式,对于支持的浏览器将会返回true,反之不支持的浏览器则会返回false

@supports 的使用

就使用方面,@supports要比@media简单和直观的多。比如下面这个小示例:

HTML 复制代码
<article class="artwork">
    <img src="myimg.jpg" alt="cityscape">
</article>

HTML的结构非常的简单。一个article标签套了一个img元素,没啥特殊之处,主要来看CSS方面的运用:

CSS 复制代码
/* 支持mix-blend-mode 浏览器将会运用的CSS规则 * /
@supports ( mix-blend-mode : overlay) {
 .artwork  img {
 mix-blend-mode : overlay;
}
}

/* 不支持mix-blend-mode 浏览器将会运用的CSS规则 */
@supports not(mix-blend-mode: overlay) {
    .artwork img {
        opacity: 0.5;
    }
}

上面的示例代码将会告诉浏览器,如果支持mix-blend-mode:overlay样式规则,将会执行@supports (...) {...}中的CSS:

CSS 复制代码
.artwork img {
    mix-blend-mode: overlay;
} 

对于不支持的浏览器则会执行@supports not(...) {...}中的CSS:

CSS 复制代码
.artwork img {
    opacity: 0.5;
}

对应的渲染效果如下图所示:

逻辑操作符

对于逻辑操作符方面@supports@media非常类似,它具有的逻辑操作符有andornot

and 逻辑操作符

and操作符相当于JavaScript逻辑操作符中的 。在CSS中的@supports逻辑符and,其意思是将两个或多个表达式连在一起,如果每个表达式返回的值都是true,则@supports的表达式就为 ;如果其中有一个为假,表达式都将返回为。比如下面这个示例,当且仅当两个原始表达式同时为真时,整个表达式才为真:

CSS 复制代码
@supports (display:table-cell) and (display: list-item) {
    /* 样式规则 */
}

or 逻辑操作符

or操作符相当于JavaScript逻辑操作符中的 。在CSS中的@supports逻辑符or,其主要用来将两个原始的表达式做逻辑或后生成一个新的表达式,如果两个原始表达式的值有一个为真或者都为真,则生成的表大家式也为真。简单地说,只要表达式中有一个为真,最终的值就是为值。比如:

CSS 复制代码
@supports (display: -webkit-flex) or (display: -moz-flex) or (display: flex) {
    /* 样式规则 */
}

not 逻辑操作符

not操作符相当于JavaScript逻辑符中的 。在CSS中的@supports逻辑符not可以放在任何表达式的前面来产生一个新的表达式,新的表达式为原表达式的值的否定(即非)。也就是说,如果表达式的值为真,加上not其新的表达式的值就是假;如果表达式的值为假,加上not其新的表达多的值就是真。

CSS 复制代码
@supports not(mix-blend-mode: overlay) {
    /* 样式规则 */
}

在CSS中的@supports的逻辑符除了单个使用之外,还可以混合在一起使用,比如:

CSS 复制代码
@supports ((margin-left: 0px) or (float:left)) and (background-color: yellow) {
    /* 样式规则 */
}

注意: 在使用andor操作符时,如果是为了定义多个表达式的执行顺序,则必须使用小括号。如果不这样做的话,该条件就是无效的,会导致整个规则失效。

@supports 对应的JavaScript API

一般情况之下,我们可以使用JavaScript来检测CSS属性是否被浏览器支持,经典的做法是使用in操作符来检测:

JavaScript 复制代码
if("backgroundColor" in document.body.style){
    document.body.style.backgroundColor = "red";
}

@supports出现之后,我们可以使用另一个API来检测浏览器是否支持CSS。而这个API就是CSS.supports,每个支持@supports规则的浏览器也将支持这个函数:

JavaScript 复制代码
if(CSS.supports("(background-color: red) and (color:white")){
    document.body.style.color = "white";
    document.body.style.backgroundColor = "red";
}

CSS.supports() 静态方法返回一个布尔值,用来检测浏览器是否支持一个给定的CSS特性。在使用当中,可以通过两种方式来调用CSS.supports()

JavaScript 复制代码
// 第一种方式
CSS.supports('mix-blend-mode', 'overlay'); // => true

// 第二种方式
CSS.supports('(mix-blend-mode: overlay)'); // => true

如果浏览器支持@supports中的条件则返回true,否则返回false

这样一来,就可以很容易根据.supports()来做一个判断,做一些事情。比如说,如果浏览器支持mix-blend-mode: luminosity 我就给目标元素添加luminosity-blend类名,否则就给目标元素添加noluminosity类名。

JavaScript 复制代码
var init = function() {
    var test = CSS.supports('mix-blend-mode', 'luminosity'),
        targetElement = document.querySelector('img');
    if (test) {
        targetElement.classList.add('luminosity-blend');
    } else {
        targetElement.classList.add('noluminosity');
    }
};
window.addEventListener('DOMContentLoaded', init, false);

有关于 @supports 更详细的介绍,请移步阅读《CSS 判断:@supports》!

条件CSS之 @viewport

先要声明一点,其实@viewport并不是真正的条件CSS规则,时至今日,@viewport规则被纳入到CSS Device Adaptation Module Level 1模块。估计大部分同学都并未接触过该规则,但对于HTML中<meta>元素的viewport应该并不会感到太陌生,即 用来设置视口

HTML 复制代码
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no">

上面的代码会指示浏览器如何对网页尺寸和缩放比例进行控制:

  • 使用元视口代码控制浏览器视口的宽度和缩放比例

  • 添加width=device-width以便与屏幕宽度(以设备无关像素为单位)进行匹配

  • 添加initial-scale=1 以便将CSS像素与设备无关像素的比例设为1:1

  • 确保在不停用用户缩放功能的情况下,你的网页也可以访问

除了设置initial-scale之外,你还可以在视口上设置以下属性:

  • minimum-scale

  • maximum-scale

  • user-scalable

但是,设置后,这些属性可以停用用户缩放视口的功能,可能会造成网页访问方面的问题。

而在CSS中,提供了一个@viewport规则,可以让我们在CSS中设置类似于<meta>标签中的viewport。简单地说,@viewport规则让我们可以对文档的大小进行设置viewport。这个特性主要被用于移动设备,但是也可以用在支持类似"固定到边缘"等特性的桌面浏览器,比如微软的Edge浏览器。

比如:

HTML 复制代码
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no" />

我们就可以使用CSS来描述,而且起到同等的效果:

CSS 复制代码
@viewport {
    width: device-width;
    initial-scale: 1;
    zoom: 1;
    min-zoom: 1;
    max-zoom: 3;
    user-zoom: fixed;
}

CSS中的@viewport规则中描述符是针对每个文档的,不涉及继承。因此,使用inherit关键词的声明将被删除。而且它的工作方式类似于@page规则,并遵循CSS的级联顺序。因此,@viewport规则中描述符将覆盖前面规则中的描述符。

正如上面的示例代码,@viewport规则中包含的规则符常见的主要有:

其他条件 CSS

上面所提到的仅是条件 CSS 中的一部分,即 CSS 的 @ 规则。在 CSS 中具备条件 CSS 的能力(类似 if...else... 功能)还有CSS 选择器,例如 :has() 、:not() ,CSS 属性,例如 CSS 自定义属性,以及 CSS 函数,比如 clamp() 、min() 和 max() 等。

使用条件 CSS 相关特性,你可以在 CSS 中只使用一个 CSS 声明做两种状态的切换,从而实现不同的效果,比如在宽屏幕上,奇数和偶数项不同的效果

如果你想一探其中究竟,请移步阅读:

扩展阅读:

小结

通过上面内容的学习,我想大家对于CSS中的@规则应该有了一个基础的了解。但该文花费更多的篇幅是来阐述条件CSS。简单的说,条件CSS主要有@media@supports规则,他们都是根据一定的条件表达式,提供相应的样式规则。当条件表达式返回的值是true,对应嵌套的CSS规则就会被浏览器渲染;反之即会忽略。而@media大多数在为响应式Web设计服务,而@supports能更好的为你给浏览器提供不同的样式规则,特别是面对于一些CSS新特性的时候,你可以借这个规则为先进的设备提供最佳、最优的样式规则;而对于不支持新特性的浏览器又能轻易的提供相应的降级样式,从而能更好的为用户提供最佳的样式规则。对于@viewport规则,也是CSS@规则中的一种,有些人也将其称为条件CSS规则的一部分,但事实它并不是真正的CSS规则。不过,它能让你在CSS样式中更好的设置视口相应的参数。

最后再提一句,不管是条件CSS还是CSS的@规则,大特定的场合下都能起到意想不到的功效。当然,大家在使用的时候,应该根据自己的场景选择最适合的方案。比如在响应式设计的时候,选择@media就较好,但对于处理刘海设备适配的时候,@supports就比较好;而对于纵向适配的时候,@supports@media结合可能会更好。所以说,没有最好的,只有最合适的。


如果你觉得该教程对你有所帮助,请给我点个赞。要是你喜欢 CSS ,或者想进一步了解和掌握 CSS 相关的知识,请关注我的专栏,或者移步阅读下面这些系列教程:

相关推荐
科技探秘人6 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人6 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR12 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香14 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969316 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai22 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_91531 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫6 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试