HTML 自定义元素子容器

HTML Custom Element Child Container

我正在尝试制作经典的菜单栏,同时尝试使用 Web 组件。我从来没有和他们一起玩过,这是我第一次涉足这个主题。它们似乎是一个非常强大的工具,但似乎没有太多关于我正在尝试做什么的信息。

我有一个自定义 html 元素树,目前看起来像这样(在调用构造函数之后):

<menu-bar>
  <sub-menu label="some label">
    <label>some label</label>
    <menu-item></menu-item>
    <menu-item></menu-item>
    <menu-item></menu-item>
  </sub-menu>
</menu-bar>

如何让自定义元素将其变成...

<menu-bar>
  <sub-menu label="some label">
    <label>some label</label>
    <div>
      <menu-item></menu-item>
      <menu-item></menu-item>
      <menu-item></menu-item>
    </div>
  </sub-menu>
</menu-bar>

...何时调用子菜单的构造函数?也可以使 div 成为阴影 dom 的一部分而菜单项元素不在阴影中 dom?

相关示例代码

class MenuBar extends HTMLElement {
  constructor() {
    super();
  }
};

class SubMenu extends HTMLElement {
  constructor() {
    super();
    let shadowRoot = this.attachShadow({mode: "open"});
    this.labelElement = document.createElement("label");
    shadowRoot.appendChild(this.labelElement);
  }

  static get observedAttributes() {
    return ["label"];
  }
  attributeChangedCallback(pName, pOldValue, pNewValue) {
    switch (pName) {
      case "label":
        this.labelElement.innerHTML = pNewValue;
        break;
    }
  }
};

class MenuItem extends HTMLElement {
  constructor() {
    super();
  }
};

window.customElements.define("menu-bar", MenuBar);
window.customElements.define("sub-menu", SubMenu);
window.customElements.define("menu-item", MenuItem);

非常感谢任何帮助,因为我只是在学习它们是如何工作的,并且正在寻找更多关于如何使用 Web 组件操作预定义 html 的详细信息,而不仅仅是直接回答这个问题例子本身。

您需要使用 <slot> 让非 shadowDOM 子元素显示在元素的 shadowDOM 中。

示例:

class MenuBar extends HTMLElement {
  constructor() {
    super();
  }
};

class SubMenu extends HTMLElement {
  constructor() {
    super();
    let shadowRoot = this.attachShadow({mode: "open"});
    this.labelElement = document.createElement("label");
    let temp = document.createElement("div");
    temp.innerHTML = '<slot></slot>';
    shadowRoot.appendChild(this.labelElement);
    shadowRoot.appendChild(temp);
  }

  static get observedAttributes() {
    return ["label"];
  }
  attributeChangedCallback(pName, pOldValue, pNewValue) {
    switch (pName) {
      case "label":
        this.labelElement.innerHTML = pNewValue;
        break;
    }
  }
};

class MenuItem extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: "open"}).innerHTML = `
    <style>
    :host {
      background-color: #eee;
      border: 1px solid #ddd;
      display:block;
      margin: 1px 0;
    }
    </style>
    <slot></slot>
    `;
  }
};

window.customElements.define("menu-bar", MenuBar);
window.customElements.define("sub-menu", SubMenu);
window.customElements.define("menu-item", MenuItem);
<menu-bar>
  <sub-menu label="some label">
    <menu-item>1</menu-item>
    <menu-item>2</menu-item>
    <menu-item>3</menu-item>
  </sub-menu>
</menu-bar>

在您的 SubMenu 中,我添加了带有 <slot><div> 来容纳所有 <menu-item> 组件。

我还添加了一些小的 CSS 以便更容易看到子元素。