@scope

可用性有限

此功能不是基线功能,因为它在一些最广泛使用的浏览器中不起作用。

@scope CSS @规则 使您能够在特定的 DOM 子树中选择元素,精确地定位元素,而无需编写过于具体的难以覆盖的选择器,并且无需将选择器过于紧密地耦合到 DOM 结构。

在 JavaScript 中,可以通过 CSS 对象模型接口 CSSScopeRule 访问 @scope

语法

@scope @规则包含一个或多个规则集(称为作用域样式规则),并定义一个作用域以将其应用于选定的元素。@scope 可以通过两种方式使用

  1. 作为 CSS 中的独立块,在这种情况下,它包含一个序言部分,其中包括作用域根和可选的作用域限制选择器——这些定义了作用域的上限和下限。
    css
    @scope (scope root) to (scope limit) {
      rulesets
    }
    
  2. 作为包含在 HTML 中的 <style> 元素内的内联样式,在这种情况下,序言被省略,并且封闭的规则集自动作用于 <style> 元素的封闭父元素。
    html
    <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 结构紧密耦合。如果您的标记结构将来发生变化,您可能需要重写 CSS。
  • 编写不太具体的内容,例如 .article-body img。但是,这将选择 section 内的所有图像。

这就是 @scope 有用的地方。它允许您定义一个精确的作用域,在该作用域内您的选择器可以定位元素。例如,您可以使用以下独立的 @scope 块来解决上述问题

css
@scope (.article-body) to (figure) {
  img {
    border: 5px solid black;
    background-color: goldenrod;
  }
}

.article-body 作用域根选择器定义了将应用规则集的 DOM 树作用域的上限,而 figure 作用域限制选择器定义了下限。因此,只有类名为 article-body<section> 内部的 <img> 元素(但不在 <figure> 元素内部)将被选中。

注意:这种具有上限和下限的作用域通常称为环形作用域

如果你想选择一个具有 article-body 类的 <section> 元素内部的所有图片,你可以省略作用域限制。

css
@scope (.article-body) {
  img {
    border: 5px solid black;
    background-color: goldenrod;
  }
}

或者,你可以在一个 <style> 元素内部内联包含你的 @scope 块,而这个 <style> 元素又位于一个具有 article-body 类的 <section> 元素内部。

html
<section class="article-body">
  <style>
    @scope {
      img {
        border: 5px solid black;
        background-color: goldenrod;
      }
    }
  </style>

  <!-- ... -->
</section>

注意: 理解这一点很重要,虽然 @scope 允许你将选择器的应用范围隔离到特定的 DOM 子树,但它并不能完全将应用的样式隔离到这些子树内。这在继承方面最为明显——子元素继承的属性(例如 colorfont-family)仍然会继承,超出任何设置的作用域限制。

:scope 伪类

@scope 块的上下文中,:scope 伪类代表作用域根——它提供了一种简单的方法,可以在作用域内部将样式应用到作用域根本身。

css
@scope (.feature) {
  :scope {
    background: rebeccapurple;
    color: antiquewhite;
    font-family: sans-serif;
  }
}

事实上,:scope 会隐式地添加到所有作用域样式规则的前面。如果你愿意,可以显式地添加 :scope 或者添加 嵌套 选择器(&)来获得相同的效果,如果你发现这些表示更容易理解的话。

以下块中的三个规则在它们选择的内容上是等价的。

css
@scope (.feature) {
  img { ... }

  :scope img { ... }

  & img { ... }
}

关于作用域选择器用法的说明

  • 作用域限制可以使用 :scope 来指定作用域限制和根之间特定的关系要求。例如
    css
    /* figure is only a limit when it is a direct child of the :scope */
    @scope (.article-body) to (:scope > figure) { ... }
    
  • 作用域限制可以使用 :scope 引用作用域根外部的元素。例如
    css
    /* figure is only a limit when the :scope is inside .feature */
    @scope (.article-body) to (.feature :scope figure) { ... }
    
  • 作用域样式规则无法逃逸子树。像 :scope + p 这样的选择是无效的,因为该选择将位于子树之外。
  • 完全可以将作用域根和限制定义为选择器列表,在这种情况下将定义多个作用域。在下面的示例中,样式应用于任何位于具有 article-heroarticle-body 类的 <section> 元素内部的 <img> 元素,但如果它嵌套在 <figure> 元素内部则不会应用。
    css
    @scope (.article-hero, .article-body) to (figure) {
      img {
        border: 5px solid black;
        background-color: goldenrod;
      }
    }
    

@scope 中的特殊性

@scope 块内包含规则集不会影响其选择器的特殊性,无论作用域根和限制内部使用什么选择器。例如

css
@scope (.article-body) {
  /* img has a specificity of 0-0-1, as expected */
  img { ... }
}

但是,如果你决定显式地将 :scope 伪类添加到你的作用域选择器前面,则需要在计算其特殊性时将其考虑在内。:scope 与所有常规伪类一样,其特殊性为 0-1-0。例如

css
@scope (.article-body) {
  /* :scope img has a specificity of 0-1-0 + 0-0-1 = 0-1-1 */
  :scope img { ... }
}

@scope 块内使用 & 选择器时,& 代表作用域根选择器;它在内部被计算为该选择器包装在一个 :is() 伪类函数中。例如,在

css
@scope (figure, #primary) {
  & img { ... }
}

& img 等价于 :is(figure, #primary) img。由于 :is() 采用其最具体的参数(在本例中为 #primary)的特殊性,因此作用域 & img 选择器的特殊性为 1-0-0 + 0-0-1 = 1-0-1。

@scope 内部 :scope& 之间的区别

:scope 代表匹配的作用域根,而 & 代表用于匹配作用域根的选择器。正因为如此,可以多次链接 &。但是,你只能使用一次 :scope——你不能在一个作用域根内部匹配另一个作用域根。

css
@scope (.feature) {
  /* Selects a .feature inside the matched root .feature */
  & & { ... }

  /* Doesn't work */
  :scope :scope { ... }
}

如何解决 @scope 冲突

@scopeCSS 级联 添加了一个新的标准:作用域邻近性。这意味着当两个作用域具有冲突的样式时,将应用到 DOM 树层次结构中到作用域根的跳跃次数最少的样式。让我们来看一个例子,看看这意味着什么。

考虑以下 HTML 代码片段,其中不同的主题卡片相互嵌套。

html
<div class="light-theme">
  <p>Light theme text</p>
  <div class="dark-theme">
    <p>Dark theme text</p>
    <div class="light-theme">
      <p>Light theme text</p>
    </div>
  </div>
</div>

如果你像这样编写主题 CSS,就会遇到麻烦。

css
.light-theme {
  background: #ccc;
}

.dark-theme {
  background: #333;
}

.light-theme p {
  color: black;
}

.dark-theme p {
  color: white;
}

最里面的段落应该被设置为黑色,因为它位于一个浅色主题卡片内。但是,它同时被 .light-theme p.dark-theme p 选中。因为 .dark-theme p 规则出现在源代码顺序的后面,所以它被应用,段落最终错误地被设置为白色。

要解决此问题,可以使用 @scope,如下所示。

css
@scope (.light-theme) {
  :scope {
    background: #ccc;
  }
  p {
    color: black;
  }
}

@scope (.dark-theme) {
  :scope {
    background: #333;
  }
  p {
    color: white;
  }
}

现在最里面的段落被正确地设置为黑色。这是因为它距离 .light-theme 作用域根只有一个 DOM 树层次结构级别,而距离 .dark-theme 作用域根有两个级别。因此,浅色样式获胜。

注意: 作用域邻近性优先于源代码顺序,但它本身会被其他优先级更高的标准覆盖,例如 重要性层级特殊性

正式语法

@scope = 
@scope [ ( <scope-start> ) ]? [ to ( <scope-end> ) ]? { <rule-list> }

示例

作用域根内的基本样式

在这个例子中,我们使用两个独立的 @scope 块分别匹配具有 .light-scheme.dark-scheme 类的元素内部的链接。请注意 :scope 如何被用来选择和为作用域根本身提供样式。在这个例子中,作用域根是应用了这些类的 <div> 元素。

HTML

html
<div class="light-scheme">
  <p>
    MDN contains lots of information about
    <a href="/en-US/docs/Web/HTML">HTML</a>,
    <a href="/en-US/docs/Web/CSS">CSS</a>, and
    <a href="/en-US/docs/Web/JavaScript">JavaScript</a>.
  </p>
</div>

<div class="dark-scheme">
  <p>
    MDN contains lots of information about
    <a href="/en-US/docs/Web/HTML">HTML</a>,
    <a href="/en-US/docs/Web/CSS">CSS</a>, and
    <a href="/en-US/docs/Web/JavaScript">JavaScript</a>.
  </p>
</div>

CSS

css
@scope (.light-scheme) {
  :scope {
    background-color: plum;
  }

  a {
    color: darkmagenta;
  }
}

@scope (.dark-scheme) {
  :scope {
    background-color: darkmagenta;
    color: antiquewhite;
  }

  a {
    color: plum;
  }
}

结果

以上代码呈现如下。

作用域根和作用域限制

在这个例子中,我们有一个 HTML 代码片段,它匹配我们在前面 描述 部分中讨论的 DOM 结构。此结构表示典型的文章摘要。需要注意的关键特征是 <img> 元素,它们在结构中的各个级别嵌套。

本例的目的是展示如何使用作用域根和限制来设置从层次结构顶部开始的 <img> 元素的样式,但只到(不包括)<figure> 元素内部的 <img> 元素——实际上创建了一个环形作用域。

HTML

html
<article class="feature">
  <section class="article-hero">
    <h2>Article heading</h2>
    <img alt="image" />
  </section>

  <section class="article-body">
    <h3>Article subheading</h3>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod
      consectetur leo, nec eleifend quam volutpat vitae. Duis quis felis at
      augue imperdiet aliquam. Morbi at felis et massa mattis lacinia. Cras
      pharetra velit nisi, ac efficitur magna luctus nec.
    </p>

    <img alt="image" />

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>

    <figure>
      <img alt="image" />
      <figcaption>My infographic</figcaption>
    </figure>
  </section>

  <footer>
    <p>Written by Chris Mills.</p>
    <img alt="image" />
  </footer>
</article>

CSS

在我们的 CSS 中,我们有两个 @scope 块。

  • 第一个 @scope 块将其作用域根定义为具有 .feature 类的元素(在本例中,仅为外部 <div>),演示了如何使用 @scope 为特定的 HTML 子集设置主题。
  • 第二个 @scope 块也将其作用域根定义为具有 .feature 类的元素,但此外还定义了 figure 的作用域限制。这确保包含的规则集将仅应用于作用域根(在本例中为 <div class="figure"> ... </div>)内的匹配元素,并且嵌套在后代 <figure> 元素内部。此 @scope 块包含一个单一的规则集,该规则集为 <img> 元素设置粗黑色边框和金色背景颜色。
css
/* Scoped CSS */

@scope (.feature) {
  :scope {
    background: rebeccapurple;
    color: antiquewhite;
    font-family: sans-serif;
  }

  figure {
    background-color: white;
    border: 2px solid black;
    color: black;
    padding: 10px;
  }
}

/* Donut scope */

@scope (.feature) to (figure) {
  img {
    border: 5px solid black;
    background-color: goldenrod;
  }
}

结果

在渲染的代码中,请注意所有 <img> 元素都使用粗边框和金色背景进行样式设置,除了 <figure> 元素内部的那个(标记为“我的信息图表”)。

规范

规范
CSS 级联和继承级别 6
# 作用域样式

浏览器兼容性

BCD 表格仅在启用 JavaScript 的浏览器中加载。

另请参阅