exportparts

**exportparts** 全局属性 允许你通过导出其 part 名称来选择和设置嵌套 影子树 中存在的元素的样式。

影子树是一个隔离的结构,其中标识符、类和样式无法被属于常规 DOM 的选择器或查询访问。有两个 HTML 属性可以应用于影子树元素,它们允许从外部目标 CSS 样式到影子树:partexportparts

全局 part 属性使影子树元素对其父 DOM 可见。part 名称用作 ::part() 伪元素的参数。通过这种方式,你可以从影子树外部应用 CSS 样式到影子树中的元素。但是,::part() 伪元素仅对父 DOM 可见。这意味着当影子树嵌套时,这些部分对除了直接父级之外的任何祖先都是不可见的。exportparts 属性解决了此限制。

exportparts 属性允许影子树部件在影子 DOM 外可见。这个概念被称为“导出”。exportparts 属性放置在元素的影子宿主上,即影子树所附加的元素。此属性的值是一个以逗号分隔的 part 名称列表,这些名称存在于影子树中。这些名称可供当前结构外部的 DOM 使用。

html
<template id="ancestor-component">
  <nested-component exportparts="part1, part2, part5"></nested-component>
</template>

导出 part 时,可以选择为该部件分配不同的名称,如下面的代码片段所示。exportparts 属性的值实际上是一个逗号分隔的部件名称映射列表。因此,上面代码片段中的 exportparts 属性等效于 exportparts="part1:part1, part2:part2, part5:part5,表示每个 part 都以相同的名称导出。在每个映射中,第一个字符串指定影子树中部件的名称,第二个字符串指定部件在外部公开的名称。

html
<template id="ancestor-component">
  <nested-component
    exportparts="part1:exposed1, part2:exposed2"></nested-component>
</template>

示例

基本组件

为了演示如何使用 exportparts 来启用对嵌套组件内部件的目标定位,我们将创建一个组件,然后将其嵌套在另一个组件中。

HTML

首先,让我们创建一个卡片组件,然后用另一个组件将其包裹起来。我们还使用了我们创建的新元素,使用纯文本作为内容填充插槽。

html
<template id="card-component-template">
  <style>
    :host {
      display: block;
    }
  </style>
  <div class="base" part="base">
    <div part="header"><slot name="header_slot"></slot></div>
    <div part="body"><slot name="body_slot"></slot></div>
    <div part="footer"><slot name="footer_slot"></slot></div>
  </div>
</template>

<card-component>
  <p slot="header_slot">This is the header</p>
  <p slot="body_slot">This is the body</p>
  <p slot="footer_slot">This is the footer</p>
</card-component>

JavaScript

我们使用 JavaScript 来定义上面 HTML 中定义的 Web 组件。

js
customElements.define(
  "card-component",
  class extends HTMLElement {
    constructor() {
      super(); // Always call super first in constructor
      const cardComponent = document.getElementById(
        "card-component-template",
      ).content;
      const shadowRoot = this.attachShadow({
        mode: "open",
      });
      shadowRoot.appendChild(cardComponent.cloneNode(true));
    }
  },
);

CSS

我们使用 ::part 伪元素来为 <card-component> 影子树的部分进行样式设置。

css
::part(body) {
  color: red;
  font-style: italic;
}

结果

嵌套组件

继续上面的 <card-component> 示例,我们通过将 <card-component> 包裹在另一个组件中来创建一个嵌套组件;在本例中,是 <card-wrapper> 组件。然后,我们使用 exportparts 属性导出要使其能够从组件的影子树外部进行样式设置的嵌套组件的部件。

HTML

html
<template id="card-wrapper">
  <style>
    :host {
      display: block;
    }
  </style>
  <card-component exportparts="base, header, body">
    <slot name="H" slot="header_slot"></slot>
    <slot name="B" slot="body_slot"></slot>
    <slot name="F" slot="footer_slot"></slot>
  </card-component>
</template>

我们包含一个 <card-wrapper> 自定义元素,以及一个 <card-component> 用于比较。

html
<h2>Card wrapper</h2>

<card-wrapper>
  <p slot="H">This is the header</p>
  <p slot="B">This is the body</p>
  <p slot="F">This is the footer</p>
</card-wrapper>

<h2>Card component</h2>

<card-component>
  <p slot="header_slot">This is the header</p>
  <p slot="body_slot">This is the body</p>
  <p slot="footer_slot">This is the footer</p>
</card-component>

JavaScript

js
customElements.define(
  "card-wrapper",
  class extends HTMLElement {
    constructor() {
      super(); // Always call super first in constructor
      const cardWrapper = document.getElementById("card-wrapper").content;
      const shadowRoot = this.attachShadow({
        mode: "open",
      });
      shadowRoot.appendChild(cardWrapper.cloneNode(true));
    }
  },
);

CSS

现在,我们可以像这样直接定位 <card-component> 的部件,以及在 <card-wrapper> 中嵌套时进行定位。

css
h2 {
  background-color: #dedede;
}

card-wrapper,
card-component {
  border: 1px dashed blue;
  width: fit-content;
}

::part(body) {
  color: red;
  font-style: italic;
}

::part(header),
::part(footer) {
  font-weight: bold;
}

结果

请注意,当嵌套时,footer 不是粗体,因为我们没有将其包含在 exportparts 中。

公开映射的部件

要重命名导出的部件,我们包含一个以逗号分隔的映射部件列表,每个映射部件都包含由冒号 (:) 分隔的原始名称和导出名称。

HTML

我们使用重新映射语法更新先前的 <card-wrapper> 自定义元素(从导出的部件列表中省略 body)。

html
<template id="card-wrapper">
  <card-component
    exportparts="
       base:card__base, 
       header:card__header, 
       footer:card__footer
     ">
    <span slot="header_slot"><slot name="H"></slot></span>
    <span slot="body_slot"><slot name="B"></slot></span>
    <span slot="footer_slot"><slot name="F"></slot></span>
  </card-component>
</template>

JavaScript

js
customElements.define(
  "card-wrapper",
  class extends HTMLElement {
    constructor() {
      super(); // Always call super first in constructor
      const cardWrapper = document.getElementById("card-wrapper").content;
      const shadowRoot = this.attachShadow({
        mode: "open",
      });
      shadowRoot.appendChild(cardWrapper.cloneNode(true));
    }
  },
);

CSS

在从 <card-wrapper> 内部定位 <card-component> 的部件时,我们只能通过其公开的部件名称来为导出的部件设置样式。

css
/* selects the exported parts name */
::part(card__header) {
  font-weight: bold;
}
/* selects nothing: these part names were not exported */
::part(footer),
::part(body) {
  font-weight: bold;
}

结果

规范

规范
CSS 影子部件
# element-attrdef-html-global-exportparts

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅