CSS @scope 规则
本文是 MDN | @scope 的翻译,并根据我自己的理解进行了适当地修改和补充。采取与原文相同的许可证。
截至 2024 年 5 月,Firefox 任然不支持 @scope
规则,但是 Chrome 与 Safari 已经支持。详情查看 Can i use @scope。
@scope
可以让我们精确的定位 DOM 子树中的元素,而无需编写难以覆盖的过于特定的选择器,并且不会将选择器与 DOM 结构耦合得太紧密。
在 JavaScript 里,可以使用 CSSScopeRule
访问 @scope
规则。
语法
@scope
包含一个或多个规则集(称为作用域样式规则),并定义将它们应用于选定元素的范围。 @scope
可以通过两种方式使用:
-
像其他 at 规则一样,作为 CSS 中的独立块,可以指定规则的作用域上限和下限。
@scope (scope root) to (scope limit) {
rulesets
} -
作为内联样式(
<style>
标签内的样式)包含在 HTML 中的,在这种情况下,两个限定参数可以被省略,并且包含的规则集自动将范围限定为<style>
元素的封闭父元素。<parent-element>
<style>
@scope {
rulesets
}
</style>
</parent-element>
描述
复杂的 Web 文档可能包括页眉、页脚、新闻文章、地图、媒体播放器、广告等组件。随着复杂性的增加,有效管理这些组件的样式变得更加困难,而通过限定样式的范围有助于我们管理这种复杂性。让我们考虑以下 DOM 树:
body
└─ article.feature
├─ section.article-hero
│ ├─ h2
│ └─ img
│
├─ section.article-body
│ ├─ h3
│ ├─ p
│ ├─ img
│ ├─ p
│ └─ figure
│ ├─ img
│ └─ figcaption
│
└─ footer
├─ p
└─ img
如果想选择带有 article-body
类的 <section>
内的 <img>
元素,可以执行以下操作:
-
编写一个选择器,例如
.feature > .article-body > img
。然而,它具有很高的特异性,因此很难被覆盖,并且与 DOM 结构紧密耦合。如果 DOM 结构发生变化,可能需要重写 CSS。 -
写一些不太具体的内容,例如
.article-body img
。但是,这将选择该部分内的所有图像。我们的目的是选择<section>
的直接子级<img>
,但是figure
内的img
也符合这个 CSS 选择器。
这就是 @scope
有用的地方。它可以定义一个精确的范围,让 CSS 选择器只作用于这个范围之内。例如,上述问题可以这么解决:
@scope (.article-body) to (figure) {
img {
border: 5px solid black;
background-color: goldenrod;
}
}
由于 .article-body
指定了 CSS 规则生效的上限,而 <figure>
指定了下限。CSS 规则只作用于 .article-body
与 <figure>
之间的元素,不会选择 <figure>
元素内的元素。
这种具有上限和下限的范围通常称为环形范围。
如果你想选择带有 article-body
类的 <section>
内的所有 图像,也可以省略范围限制:
@scope (.article-body) {
img {
border: 5px solid black;
background-color: goldenrod;
}
}
或者,你可以采用刚刚提到的第二种写法,将 @scope
块内联包含在 <style>
元素内,该元素又位于 <section>
内:
<section class="article-body">
<style>
@scope {
img {
border: 5px solid black;
background-color: goldenrod;
}
}
</style>
<!-- ... -->
</section>
需要注意的是,虽然 @scope
将选择器的应用隔离到特定的 DOM 子树,但它并不能完全隔离这些子树内应用的样式。这在继承中最为明显——子级继承的属性(例如颜色或字体系列)仍然会被继承,超出任何设置的范围限制。
:scope
伪类
在 @scope
块的上下文中,:scope
伪类代表作用域根,它提供了一种从作用域内部将样式应用到作用域根本身的简单方法:
@scope (.feature) {
:scope {
background: rebeccapurple;
color: antiquewhite;
font-family: sans-serif;
}
}
事实上,会被 :scope
隐式地添加到所有作用域样内的式规则的前面。也就是说,任何 @scope
作用域内的 CSS 选择器前面都会被浏览器自动加上 :scope
,但是不计入 CSS 特异性。
以下的三条规则是等价的 (但特异性不相等,查看@scope
的特异性):
@scope (.feature) {
img { ... }
:scope img { ... }
& img { ... }
}
注意事项
-
范围限制可以使用
:scope
来指定范围限制和根之间的特定关系要求。例如:/* 指定下限为 .article-body 的直接 figure 子级 */
@scope (.article-body) to (:scope > figure) { ... } -
范围限制可以使用
:scope
引用范围根之外的元素,也就是说上下限都是单纯的 CSS 选择器,并不要求下限必须从上限开始选择。例如:@scope (.article-body) to (.feature :scope figure) { ... }
-
下限必须是上限的子孙元素(原文是:Scoped style rules can't escape the subtree)。像
:scope + p
这样的选择是无效的,因为该选择将位于上限的子树之外。 -
选择器的上限可以指定多个,在这种情况下将视为定义多个范围。在以下示例中,样式将应用于
<section>
内具有article-hero
或article-body
类的任何<img>
,而如果<img>
嵌套在<figure>
内,则不会被选中:@scope (.article-hero, .article-body) to (figure) {
img {
border: 5px solid black;
background-color: goldenrod;
}
}
@scope
的CSS特异性
简单来说, @scope
不会改变其内部规则的特异性,例如:
@scope (.article-body) {
/* img 的特异性是 0-0-1 */
img { ... }
}