exportparts

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

¥The exportparts global attribute allows you to select and style elements existing in nested shadow trees, by exporting their part names.

影子树是一种孤立的结构,其中属于常规 DOM 的选择器或查询无法访问标识符、类和样式。有两个 HTML 属性可应用于影子树元素,从而能够将 CSS 样式从外部定向到影子树:partexportparts

¥The shadow tree is an isolated structure where identifiers, classes, and styles cannot be reached by selectors or queries belonging to a regular DOM. There are two HTML attributes that can be applied to shadow tree elements that enable targeting CSS styles from outside to shadow tree: part and exportparts.

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

¥The global part attribute makes a shadow tree element visible to its parent DOM. A part name is used as the parameter of the ::part() pseudo-element. In this way, you can apply CSS styles to elements in the shadow tree from outside of it. However, the ::part() pseudo-element is only visible to the parent DOM. This means that when a shadow tree is nested, the parts are not visible to any ancestors other than the direct parent. The exportparts attribute solves this limitation.

exportparts 属性使影子树部分在影子 DOM 之外可见。这个概念被称为 "exporting"。exportparts 属性放置在元素的影子主机上,即影子树所附加到的元素。该属性的值是影子树中存在的 part 名称的逗号分隔列表。这些名称可供当前结构之外的 DOM 使用。

¥The exportparts attribute enables shadow tree parts to be visible outside the shadow DOM. This concept is referred to as "exporting". The exportparts attribute is placed on the element's shadow host, which is the element to which the shadow tree is attached. The value of this attribute is a comma-separated list of part names present in the shadow tree. These names are made available to the DOMs outside the current structure.

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 都是以相同的名称导出的。在每个映射中,第一个字符串指定影子树内部件的名称,第二个字符串指定该部件将用于外部公开的名称。

¥When exporting a part, you have the option to assign a different name to the part, as shown in the snippet below. The value of the exportparts attribute is really a comma-separated list of part-name mappings. So the exportparts attribute in the above code snippet is the equivalent of exportparts="part1:part1, part2:part2, part5:part5, indicating that each part is exported with the same name. In each mapping, the first string specifies the name of the part within the shadow tree, and the second string specifies the name with which the part will be exposed externally.

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

示例

¥Examples

基本组成

¥Basic component

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

¥To demonstrate how exportparts is used to enable targeting parts within nested components, we will create a component, and then nest it within another component.

HTML

首先,我们创建一个卡片组件,然后用另一个组件封装它。我们还使用我们创建的新元素,用纯文本作为内容填充插槽。

¥First, let's create a card component that we will then wrap with another component. We also use the new element we created, populating the slots with plain text as content.

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 组件:

¥We use JavaScript to define our web component defined in the HTML above:

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> 影子树的各个部分进行样式设置:

¥We style parts of the <card-component> shadow tree using the ::part pseudo-element:

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

结果

¥Results

嵌套组件

¥Nested component

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

¥Continuing the above <card-component> example, we create a nested component by wrapping the <card-component> within another component; in this case, the <card-wrapper> component. We then export the parts from the nested component that we want to make styleable from outside the component's shadow tree with the exportparts attribute.

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>
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> 进行比较:

¥We include a <card-wrapper> custom element, and a <card-component> for comparison:

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-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));
    }
  },
);
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> 中时,如下所示:

¥Now, we can target parts of the <card-component> directly and when nested within a <card-wrapper> like so:

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;
}

结果

¥Results

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

¥Note footer is not bold when nested, as we did not include it in exportparts.

暴露映射的部分

¥Exposing mapped parts

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

¥To rename exported parts, we include a comma-separated list of mapped parts, with each mapped part including the original name and exported name separated by a colon (:):

HTML

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

¥We update the prior <card-wrapper> custom element with the remapping syntax (omitting body from the exported parts list):

html
<template id="card-component-template">
  <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-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>
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-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));
    }
  },
);
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> 的部件时,我们只能通过导出的部件名称来设置导出部件的样式:

¥In targetting the parts of the <card-component> from within the <card-wrapper>, we can only style the exported parts via their exposed part names:

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;
}

结果

¥Results

规范

Specification
CSS Shadow Parts
# element-attrdef-html-global-exportparts

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看

¥See also