Element: attachShadow() 方法

Baseline 广泛可用 *

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2020 年 1 月⁩ 起,所有主流浏览器均已支持。

* 此特性的某些部分可能存在不同级别的支持。

Element.attachShadow() 方法将一个 Shadow DOM 树附加到指定的元素,并返回对其 ShadowRoot 的引用。

可以附加 Shadow DOM 的元素

请注意,你不能将 Shadow Root 附加到每种类型的元素上。出于安全原因,有些元素不能拥有 Shadow DOM(例如 <a>)。

以下是你可以附加 Shadow Root 的元素列表:

在一个已经是 Shadow Host 的元素上调用此方法

此方法可以在已具有声明式 Shadow Root的元素上调用,前提是指定的模式 mode 与现有模式匹配。在这种情况下,已经存在的 ShadowRoot 将被清除并返回。这允许一些情况,例如,服务器端渲染已经声明式地创建了一个 Shadow Root,然后客户端代码尝试再次附加该根。

否则,在一个已经有 Shadow Root 的元素上调用 attachShadow() 将会抛出异常。

语法

js
attachShadow(options)

参数

options

一个包含以下字段的对象:

模式

一个字符串,指定 Shadow DOM 树的封装模式。可以是以下之一:

open

Shadow Root 的元素可以从根外部的 JavaScript 访问,例如使用 Element.shadowRoot

js
element.attachShadow({ mode: "open" });
element.shadowRoot; // Returns a ShadowRoot obj
closed

禁止从外部 JavaScript 访问封闭 Shadow Root 的节点。

js
element.attachShadow({ mode: "closed" });
element.shadowRoot; // Returns null
clonable 可选

一个布尔值,指定 Shadow Root 是否可克隆:当设置为 true 时,使用 Node.cloneNode()Document.importNode() 克隆的 Shadow Host 将在副本中包含 Shadow Root。其默认值为 false

delegatesFocus 可选

一个布尔值,当设置为 true 时,指定缓解自定义元素可聚焦性问题的行为。当 Shadow DOM 中不可聚焦的部分被点击时,第一个可聚焦的部分将获得焦点,并且 Shadow Host 将获得任何可用的 :focus 样式。其默认值为 false

serializable 可选

一个布尔值,当设置为 true 时,表示 Shadow Root 是可序列化的。如果设置,Shadow Root 可以通过调用 Element.getHTML()ShadowRoot.getHTML() 方法并将 options.serializableShadowRoots 参数设置为 true 来序列化。其默认值为 false

slotAssignment 可选

一个字符串,指定 Shadow DOM 树的插槽分配模式。可以是以下之一:

命名

元素会自动分配到此 Shadow Root 中的 <slot> 元素。宿主中带有 slot 属性的任何后代,如果其值与此 Shadow Root 中 <slot>name 属性匹配,将被分配到该插槽。宿主中没有 slot 属性的任何顶级子元素,如果存在没有 name 属性的 <slot>(“默认插槽”),将被分配到该插槽。

manual

元素不会自动分配到 <slot> 元素。相反,它们必须通过 HTMLSlotElement.assign() 手动分配。其默认值为 named

返回值

返回一个 ShadowRoot 对象。

异常

NotSupportedError DOMException

当你尝试将 Shadow Root 附加到以下元素时,可能会抛出此错误:

  • 不在 HTML 命名空间中或无法附加 Shadow DOM 的元素。
  • 其中元素定义静态属性 disabledFeatures 的值已设置为 "shadow"
  • 已经有一个未声明式创建的 Shadow Root 的元素。
  • 具有声明式 Shadow Root但指定 mode 与现有模式不匹配的元素。

示例

字数统计自定义元素

以下示例取自我们的 word-count-web-component 演示(也可在线查看)。你可以看到我们在代码中间使用 attachShadow() 创建了一个 Shadow Root,然后将我们自定义元素的内容附加到其中。

js
// Create a class for the element
class WordCount extends HTMLParagraphElement {
  constructor() {
    // Always call super first in constructor
    super();

    // count words in element's parent element
    const wcParent = this.parentNode;

    function countWords(node) {
      const text = node.innerText || node.textContent;
      return text
        .trim()
        .split(/\s+/g)
        .filter((a) => a.trim().length > 0).length;
    }

    const count = `Words: ${countWords(wcParent)}`;

    // Create a shadow root
    const shadow = this.attachShadow({ mode: "open" });

    // Create text node and add word count to it
    const text = document.createElement("span");
    text.textContent = count;

    // Append it to the shadow root
    shadow.appendChild(text);

    // Update count when element content changes
    this.parentNode.addEventListener("input", () => {
      text.textContent = `Words: ${countWords(wcParent)}`;
    });
  }
}

// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });

禁用 Shadow DOM

如果元素有一个名为 disabledFeatures 的静态属性,它是一个包含字符串 "shadow" 的数组,那么 attachShadow() 调用将抛出异常。

例如

js
class MyCustomElement extends HTMLElement {
  // Disable shadow DOM for this element.
  static disabledFeatures = ["shadow"];

  constructor() {
    super();
  }

  connectedCallback() {
    // Create a shadow root.
    // This will throw an exception.
    const shadow = this.attachShadow({ mode: "open" });
  }
}

// Define the new element
customElements.define("my-custom-element", MyCustomElement);

规范

规范
DOM
# dom-element-attachshadow

浏览器兼容性

另见