主流浏览器都支持原生 CSS 嵌套了!

开发 前端
现在很多前端项目都还在借助CSS预处理器来编写 CSS,以添加一下实用的功能到 CSS 中,例如 mixin、函数、嵌套等功能。在应用的构建过程中,会将这些代码转换为常规的 CSS,以便浏览器可以理解它。

8 月 29 日,Firefox 117版本发布,该版本增加了对 CSS 原生嵌套的支持。至此,所有主流桌面浏览器现在都已经支持原生 CSS 嵌套语法!

图片图片

注意,有些移动浏览器还不支持该语法,不过,这些浏览器合计只占约全球浏览器市场份额的 3%。

之前,我们需要借助预处理器(SCSS/Less)来编写嵌套语法。现在,CSS 终于原生支持嵌套语法了。下面就来看看原生 CSS 嵌套是怎么用的,未来是否还需要使用预处理器?

CSS 嵌套入门

使用 CSS 嵌套,可以编写更少的代码,并且代码更易于阅读和维护。没有CSS嵌套时,我们只能这样输入完成的选择器路径:

.parent1 .child1,
.parent2 .child1 {
  color: red;
}

.parent1 .child2,
.parent2 .child2 {
  color: green;
}

.parent1 .child2:hover,
.parent2 .child2:hover {
  color: blue;
}

使用新的嵌套语法,可以将子选择器嵌套在父选择器中:

.parent1, .parent2 {

  .child1 {
    color: red;
  }

  .child2 {
    color: green;

    &:hover {
      color: blue;
    }
  }
}

我们可以将选择器嵌套任意深度,但是最好不要超过三层,虽然没有技术上的硬性限制,但是嵌套深度越大,会使代码更难读,并且生成的 CSS 可能会变得不必要的冗长。

CSS 嵌套语法

可以将任何选择器嵌套在另一个选择器中,但它必须以符号开头,例如 &、.(用于HTML类)、#(用于HTML id)、@(用于媒体查询)、:、::、*、+、~、> 或 [。换句话说,它不能直接引用HTML元素。下面这段代码是无效的,

选择器不会被解析。

.parent1 {
  p {
    color: blue;
  }
}

需要使用 & 符号来引用当前选择器:

.parent1 {
  & p {
    color: blue;
  }
}

另外,还可以使用以下任一方法:

  • > p:只会为 .parent1 的直接子元素设置样式
  • :is(p):使用最具体选择器的特异性
  • :where(p):特异性为零

在这个例子中,它们都可以正常工作,但在更复杂的样式表中,可能会遇到特异性问题。

& 还允许在父选择器上针对伪元素和伪类进行定位,例如:

p.my-element {

  &::after {}

  &:hover {}

  &:target {}

}

如果不使用&符号,将会选择该选择器的所有子元素,而不是p.my-element。(在Sass中也是如此。)

可以在选择器的任何位置使用&,例如:

.child1 {
  .parent3 & {
    color: red;
  }
}

这将转换为以下非嵌套语法:

.parent3 .child1 { color: red; }

我们甚至可以在选择器中使用多个&符号:

ul {
  & li & {
    color: blue;
  }
}

这将选择嵌套的<ul>元素(ul li ul),但不建议这样做,因为这样代码会变得混乱。

除此之外,还可以嵌套媒体查询。以下代码将为段落元素应用青色,当浏览器窗口宽度至少为800像素时,将会覆盖该样式,并将段落元素的颜色更改为紫色。

p {
  color: cyan;

  @media (min-width: 800px) {
    color: purple;
  }
}

CSS 嵌套陷阱

原生嵌套将父选择器封装在:is()中,这可能会导致与Sass输出的差异。

考虑下面的代码:

.parent1, #parent2 {
  .child1 {}
}

当在浏览器中解析时,这会变成了以下内容:

:is(.parent1, #parent2) .child1 {}

由于原生嵌套使用了:is()并根据其最具体选择器(#parent2)的优先级来确定优先级,因此在.parent1内部的.child1元素具有101的优先级。而对于Sass编译,会将相同的代码编译成不同的结构。

Sass 会将相同的代码编译为以下内容:

.parent1 .child1,
#parent2 .child1 {

}

在这种情况下,.parent1内部的.child1元素的优先级为002,因为它匹配了两个类(#parent2被忽略)。它的选择器比原生选项更不具体,因此在级联中被覆盖的可能性较大。

还可能遇到一个更微妙的问题,考虑以下代码:

.parent .child {

  .grandparent & {}

}

原生CSS的等效代码如下:

.grandparent :is(.parent .child) {}

这将匹配以下顺序错误的HTML元素:

<div class="parent">
  <div class="grandparent">
    <div class="child">MATCH</div>
  </div>
</div>

MATCH 被应用样式是因为CSS解析器执行了以下操作:

  1. 它找到所有具有class为child的元素,并且这些元素在DOM层次结构中的任意位置都有一个祖先元素为parent。
  2. 找到包含MATCH的元素后,解析器检查是否存在一个祖先元素为grandparent,同样是在DOM层次结构的任意位置。它找到了一个并相应地给该元素应用样式。

但在Sass中,情况并非如此,它编译成以下内容:

.grandparent .parent .child {}

由于HTML元素的类没有遵循严格的grandparent、parent和child的顺序,因此上面的HTML不会被应用样式。

最后,Sass 使用字符串替换,因此以下声明也是有效的,并且匹配任何具有outer-space类的元素:

.outer {
  &-space { color: black; }
}

原生 CSS 会忽略&-space选择器。

还需要预处理器吗?

现在很多前端项目都还在借助CSS预处理器来编写 CSS,以添加一下实用的功能到 CSS 中,例如 mixin、函数、嵌套等功能。在应用的构建过程中,会将这些代码转换为常规的 CSS,以便浏览器可以理解它。

Sass 开发团队也宣布他们将支持在.css文件中使用原生CSS嵌套,并保持代码不变。他们将继续像以前一样编译嵌套的SCSS代码,以避免破坏现有的代码库,但在全局浏览器支持达到98%时,将开始使用:is()选择器。

现在,越来越多预处理器带来的功能都在原生 CSS 中内置了,嵌套语法就是其中之一。虽然,嵌套语法已被主流浏览器支持,但是很多用户还在使用旧版本的浏览器,无法很好的得到新语法的支持,并且个人认为,预处理器的嵌套语法相比原生嵌套语法更简单易懂。所以,在很长一段时间内,我们仍然是需要借助预处理器来编写 CSS。

除此之外,我们仍然需要借助 PostCSS 等后处理器来处理新的 CSS 功能,以将其转化为浏览器可以理解的内容。

责任编辑:武晓燕 来源: 前端充电宝
相关推荐

2023-09-05 09:40:55

SCSS预处理器

2010-08-19 15:47:34

CSS Reset浏览器

2015-01-21 15:45:50

斯巴达浏览器

2023-03-01 00:06:14

JavaScrip框架CSS

2013-10-30 16:44:47

网站名片浏览器

2010-04-01 13:03:10

2012-11-27 11:22:06

浏览器

2009-07-24 15:29:11

支持CSS3

2011-02-23 10:17:49

浏览器OperaSafari

2009-04-16 08:30:59

2009-03-12 08:52:12

浏览器竞争

2013-02-21 15:56:18

浏览器遨游

2009-03-19 09:16:36

浏览器性能测试Chrome

2011-08-10 14:05:53

浏览器

2023-01-27 09:14:35

CSS原生嵌套

2016-10-29 10:36:40

H5原生工信部

2010-09-15 15:39:03

CSS hack

2010-07-06 13:20:22

Opera 10.60

2009-05-14 09:25:09

微软Windows 7浏览器

2009-05-27 08:52:05

点赞
收藏

51CTO技术栈公众号