HTML exportparts 全域性屬性

Baseline 已廣泛支援

此特性已經十分成熟,可在許多裝置和瀏覽器版本上使用。自 2020 年 7 月以來,它已在各大瀏覽器中可用。

exportparts 全域性屬性允許您透過匯出其 part 名稱來選擇和樣式化巢狀 Shadow Tree 中存在的元素。

Shadow Tree 是一個隔離的結構,其中識別符號、類和樣式無法透過屬於常規 DOM 的選擇器或查詢訪問。有兩個 HTML 屬性可以應用於 Shadow Tree 元素,它們可以從外部定位 Shadow Tree 的 CSS 樣式:partexportparts

全域性 part 屬性使 Shadow Tree 元素對其父 DOM 可見。part 名稱用作 ::part() 偽元素的引數。透過這種方式,您可以從 Shadow Tree 外部將其 CSS 樣式應用於 Shadow Tree 中的元素。但是,::part() 偽元素僅對父 DOM 可見。這意味著,當 Shadow Tree 巢狀時,這些部分對於直接父級以外的任何祖先都是不可見的。exportparts 屬性解決了此限制。

exportparts 屬性使 Shadow Tree 的部分在 Shadow DOM 外部可見。這個概念被稱為“匯出”。exportparts 屬性放置在元素的shadow host上,即 Shadow Tree 所附加的元素。此屬性的值是 Shadow Tree 中存在的 part 名稱的逗號分隔列表。這些名稱可供當前結構外部的 DOM 使用。

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

匯出 part 時,您可以選擇為該部分分配不同的名稱,如下面的程式碼片段所示。exportparts 屬性的值實際上是 part 名稱對映的逗號分隔列表。因此,上面的程式碼片段中的 exportparts 屬性等同於 exportparts="part1:part1, part2:part2, part5:part5,表示每個 part 都以相同的名稱匯出。在每個對映中,第一個字串指定 Shadow Tree 中部分的名稱,第二個字串指定該部分將在外部暴露的名稱。

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> Shadow Tree 的部分

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

結果

巢狀元件

在上面的 <card-component> 示例的基礎上,我們透過將 <card-component> 包裝在另一個元件中來建立一個巢狀元件;在這種情況下,是 <card-wrapper> 元件。然後,我們匯出巢狀元件中那些我們希望從元件 Shadow Tree 外部進行樣式設定的部分,使用 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

瀏覽器相容性

另見