将近20年,CSS终于在所有现代浏览器实现了原生嵌套语法!!!

本文探讨了CSS原生嵌套语法的兴起,从早期开发者对类名重复和缺乏命名空间的抱怨,到变量和嵌套需求推动下对预处理器使用的减少。CSS原生嵌套有助于组织代码、减少冗余,尽管存在浏览器兼容性考量,但它逐渐成为现代CSS的最佳实践。
摘要由CSDN通过智能技术生成
5e73220a1579464701299cb89c40034e.jpeg
image.png

引言

将近20年,CSS终于在所有现代浏览器实现了原生嵌套语法,是时候淘汰less/sass等预处理器了

288e77178db9e9f2ce41eb024436b4e4.png
image.png

CSS原生嵌套浏览器兼容性查看

来自 Web 开发者的呼声

2012年4月13日,CodePen 的联合创始人 Chris Coyier 抱怨 CSS 的类名不支持命名空间,导致要写好多重复的选择器。

2016年2月2日,微软的项目经理 Kenneth Auchenberg 说如果 CSS 支持了变量和嵌套,他将不再使用预处理器。

2016年12月8日,《CSS揭秘》的作者 Lea Verou 调研了使用 CSS 预处理器的首要原因(单选题),有 1838 个人参与了投票,最终并列第一的两个理由是嵌套和变量。她觉得是时候该重新考虑 CSS 原生嵌套的问题了。

2017年7月13日,集设计和开发才能于一身的 UI/UX 自由工作者 Sara Soueidan 说嵌套是她最想要的 CSS 功能。

2017年8月15日,node-inspect 的作者 Jan Olaf Krems 说 cssnext 把嵌套定义成了“明天的 CSS”,但他还是想看到原生的 CSS 嵌套,毕竟 JS 的生态系统已经证明避免“每个人都使用自己的半标准语言”绝对是健康的。

2018年2月23日,Lea Verou 再次发声,说她现在还在用 CSS 预处理器写嵌套,一旦 CSS 支持了原生嵌套,她就果断弃用预处理。

2018年5月25日,postcss-preset-env 的作者 Jonathan Neal 再次提议重新考虑下让 CSS 支持原生嵌套(也就是本文章的切入点),这引来了一波热议。

在嵌套之前,每个选择器都需要单独声明,相互之间没有联系。这会导致重复、样式表冗余和开发体验分散。

示例

嵌套之前的示例:

.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

嵌套之后,选择器可以继续编写,并且与之相关的样式规则可以在其中进行分组。

嵌套之后的示例:

.nesting {
    color: hotpink;
    
    >.is {
        color: rebeccapurple;

        >.awesome {
            color: deeppink;
        }
    }
}

jcode

嵌套可以帮助开发人员减少重复选择器的需求,同时还可以将相关元素的样式规则放在一起,提高样式与目标HTML匹配的能力。如果在前面的示例中删除了.nesting组件,您可以删除整个嵌套组,而不是在文件中搜索相关的选择器实例。

嵌套的作用

嵌套可以帮助:

  • 组织代码

  • 减小文件大小

  • 进行重构

嵌套从Chrome 112版本开始提供支持,并且在Safari技术预览版162中也可以尝试使用。

开始使用CSS嵌套

在本文的其余部分,我们将使用以下演示沙箱来帮助您可视化所选择的内容。在默认状态下,没有选择任何内容,所有内容都可见。通过选择不同的形状和大小,您可以练习语法并查看其效果。

c19796ca038e9d6146688793b7f5747f.png
一组小圆、大圆、小三角形、大三角形和正方形的彩色网格。

沙箱中有圆、三角形和正方形。其中一些是小的,中等的或大的。其他的是蓝色的、粉色的或紫色的。它们都在.demo容器元素内。以下是您将要选择的HTML元素的预览。

<div class="demo">
  <div class="sm triangle pink"></div>
  <div class="sm triangle blue"></div>
  <div class="square blue"></div>
  <div class="sm square pink"></div>
  <div class="sm square blue"></div>
  <div class="circle pink"></div>
  …
</div>

嵌套示例

CSS嵌套允许您在一个选择器的上下文中定义另一个选择器的样式。

.parent {
  color: blue;

  .child {
    color: red;
  }
}

在此示例中,.child 类选择器嵌套在.parent类选择器内部。这意味着嵌套的.child选择器只会应用于具有.parent类的元素的子元素。

这个示例也可以使用&符号来显式表示父类应该放置在哪里。

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

这两个示例在功能上是等效的,并且可以通过更多高级示例来进一步理解它们的用法。

选择圆形

对于第一个示例,任务是为演示中的圆形添加淡化和模糊样式。

不使用嵌套,CSS现在的写法:

.demo .circle {
  opacity: .25;
  filter: blur(25px);
}

使用嵌套,有两种有效的方式:

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

或者

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

结果,所有具有.circle类的元素在.demo内部被模糊处理,几乎不可见:

7f985f4a6159791435a8ba56c05e3230.png
彩色网格中的圆形已经消失,几乎看不到了。

jcode

选择任何三角形和正方形

这个任务需要选择多个嵌套元素,也称为组选择器。

不使用嵌套,现在的CSS有两种方式:

.demo .triangle,
.demo .square {
  opacity: .25;
  filter: blur(25px);
}

或者,使用:is()

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

使用嵌套,有两种有效的方式:

.demo {
  & .triangle,
  & .square {
    opacity: .25;
    filter: blur(25px);
  }
}

或者

.demo {
  .triangle, .square {
    opacity: .25;
    filter: blur(25px);
  }
}

这两种嵌

套选项在内部都使用了:is()

.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

结果.demo内部只剩下.circle元素:

1e1f9868317961d2c6c2f9e8badd0931.png
彩色网格只剩下圆形,其他形状几乎不可见。

jcode

选择大三角形和大圆形

这个任务需要使用复合选择器,元素必须同时具有两个类才能被选中。

不使用嵌套,现在的CSS写法:

.demo .lg.triangle,
.demo .lg.square {
 opacity: .25;
 filter: blur(25px);
}

或者

.demo .lg:is(.triangle, .circle) {
  opacity: .25;
  filter: blur(25px);
}

使用嵌套,有两种有效的方式:

.demo {
  .lg.triangle,
  .lg.circle {
    opacity: .25;
    filter: blur(25px);
  }
}

或者

.demo {
  .lg {
    &.triangle,
    &.circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

结果,所有大三角形和大圆形在.demo内部被隐藏:

cf8e03b323cae8d1284d63c29fabfc6a.png
彩色网格只显示了小和中等形状。

试试演示

使用复合选择器和嵌套的专业提示

&符号在这里非常有用,因为它明确显示了如何连接嵌套选择器。考虑以下示例:

.demo {
  .lg {
    .triange,
    .circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

虽然这是一种有效的嵌套方式,但结果可能不符合您的预期。原因是,如果没有&来指定期望的.lg.triangle, .lg.circle组合,实际结果将是.lg .triangle, .lg .circle;后代选择器。

在没有&的情况下嵌套类始终会生成后代选择器。使用&符号可以更改结果。

选择除了粉色形状之外的所有形状

这个任务需要使用否定功能伪类,元素不能具有指定的选择器。

不使用嵌套,现在的CSS写法:

.demo :not(.pink) {  opacity: .25;  filter: blur(25px);}

使用嵌套,有两种有效的方式:

.demo :not(.pink) {
  opacity: .25;
  filter: blur(25px);
}

或者

.demo {
  :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

结果,所有不是粉色的形状在.demo内部被隐藏:

8380fdb7b3dbb94628a6422ced448bc2.png
彩色网格现在是单色的,只显示粉色形状。

jcode

使用&进行精确控制和灵活性

假设您想要选择.demo元素,并使用:not()选择器。这时就需要使用&

.demo {
  &:not() {
    ...
  }
}

这将.demo:not()合并为.demo:not(),与之前的示例.demo :not()不同。这一点非常重要,当您想要嵌套:hover交互时,需要特别注意。

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

更多嵌套示例

CSS嵌套规范中包含了更多示例。如果您想通过示例了解更多关于语法的内容,该规范涵盖了各种有效和无效的示例。

接下来的几个示例将简要介绍CSS嵌套的更多特性,帮助您了解其广泛的功能。

嵌套@media

在样式表中,如果要修改选择器及其样式的媒体查询条件位于不同的地方,这可能会分散注意力。使用嵌套,您可以将媒体查询条件直接嵌套在上下文中。

为了方便起见,如果嵌套的媒体查询仅修改当前选择器上下文的样式,则可以使用最简化的语法。

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    font-size: 1.25rem;
  }
}

使用&也可以:

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    &.large {
      font-size: 1.25rem;
    }
  }
}

这个示例展示了使用&的扩展语法,同时也针对.large

卡片进行了定位,以展示继续工作的其他嵌套特性。

了解更多关于嵌套@media的内容。

任意嵌套

到目前为止,所有的示例都是在前一个上下文中继续或附加选择器。如果需要,您可以完全更改或重新排列上下文。

.card {
  .featured & {
    /* .featured .card */
  }
}

&符号表示选择器对象的引用(不是字符串),可以放置在嵌套选择器的任何位置。甚至可以多次放置:

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

虽然这个示例看起来有点没有用,但在某些情况下,能够重复选择器上下文是很方便的。

无效的嵌套示例

在嵌套中,有几种语法场景是无效的,如果您之前使用预处理器进行嵌套,可能会对此感到惊讶。关于有效嵌套语法的速查表可以在本文的理解嵌套解析器部分找到。

嵌套元素标签名

HTML元素目前需要在前面加上&符号或使用:is()进行包装。

.card {
  h1 {
    /* 🛑 h1 does not start with a symbol */
  }
}

通过以下语法进行修复:

.card {
  & h1 {
    /* ✅ now h1 starts with a symbol */
  }

  /* or */

  :is(h1) {
    /* ✅ now h1 starts with a symbol */
  }
}
嵌套和连接

许多CSS类命名约定依赖于嵌套能够像连接字符串一样连接或附加选择器。但在CSS嵌套中,这种方法是无效的,因为选择器不是字符串,而是对象引用。

.card {
  &--header {
    /* is not equal to ".card--header" */
  }
}

更详细的解释可以在规范中找到。

混合嵌套和声明

考虑以下嵌套的CSS块:

.card {
  color: green;
  & { color: blue; }
  color: red;
}

.card元素的颜色将是蓝色。

所有混合的样式声明都会被提升到顶部,就好像它们是在嵌套之前编写的一样。更多细节可以在规范中找到。

理解嵌套解析器

要在CSS嵌套中取得最好的效果,我们可以研究解析器在处理嵌套时的工作原理。了解这一点,我们可以自信地嵌套样式,而不必经常查找规则。

首先,最简单的方法是识别触发解析器开始消耗嵌套样式的符号。

& @ : . > ~ + # [ *

这些符号应该看起来很熟悉。其中一些是组合器,一些是选择器。所以最简单的情况是,如果解析器发现您的嵌套选择器,并且它不以这些符号之一开头,它将失败,并错误地消耗您的样式。

![& @ : . > ~ + #  *. 嵌套选择器以这些符号之一开头吗?如果是,则有效。如果不是,则无效。

a38952b1872b9b7b412561918d698a71.png
image.png

功能检测

有两种非常好的方法来检测CSS嵌套:使用嵌套或使用@supports检查

使用嵌套:

html {
  .has-nesting {
    display: block;
  }

  .no-nesting {
    display: none;
  }
}

使用@supports检查:

@supports not ((nesting: normal)) {  /* 在这里使用传统的样式 */}

这两种方法都依赖于嵌套的nesting功能。在支持的浏览器中,第一个嵌套示例将起作用,而第二个示例将被忽略。在不支持嵌套的浏览器中,情况正好相反。

总结

CSS嵌套使开发者能够以更直观和组织良好的方式编写样式规则。它有助于减少代码重复、提高可读性,并提供更好的维护性和重构能力。

使用嵌套时,确保理解如何正确放置选择器和使用&符号来连接或附加选择器。此外,熟悉无效的嵌套示例,以避免错误。

在实际使用CSS嵌套时,请记住进行功能检测,并考虑适当的回退或替代方案,以确保在不支持嵌套的浏览器中也能提供一致的体验。

最后,请记住,嵌套应该是有意义的,并且应该提高代码的可读性和可维护性。避免过度使用嵌套,以免引入样式的复杂性和性能问题。使用嵌套时,请遵循一致的命名约定和最佳实践,以确保团队成员可以轻松理解和维护代码。

参考

  • https://tabatkins.github.io/specs/css-nesting/

  • https://developer.chrome.com/articles/css-nesting/#understanding-the-nesting-parser

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值