如何在没有 attachShadow 的情况下创建自定义元素?

Rya*_*hel 1 html javascript web-component shadow-dom

假设我有一些这样的代码:

class MyElem extends HTMLElement {
  constructor() {
    super();
    
    let templateContent = document.getElementById('template-elem').content;
    this.innerHTML = templateContent.cloneNode(true);
  }
}

window.customElements.define('my-elem', MyElem);
Run Code Online (Sandbox Code Playgroud)
<template id="template-elem">
  <div class="a">
    <div class="b">b</div>
    <div class="c">c</div>
  </div>
</template>

<my-elem></my-elem>
Run Code Online (Sandbox Code Playgroud)

为什么这不起作用?在 Chrome 检查器中,自定义元素内部没有 HTML。我也试过这样做:

this.append(templateContent.cloneNode(true)); 
Run Code Online (Sandbox Code Playgroud)

但这也导致了一个空的 HTML 树。

所有教程都提到使用 shadow DOM,如下所示:

this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true));
Run Code Online (Sandbox Code Playgroud)

虽然这样做有效,但它会强制您将 Shadow DOM 用于您的自定义元素。有没有办法在不需要使用 Shadow DOM 的情况下将模板的 HTML 附加到您的自定义元素?我更喜欢在我的小用例中使用全局 CSS 样式。

Dan*_*man 5

你掉进了多个陷阱,就像每个人第一次组件冒险一样。

  1. 自定义元素(严格来说只有带有shadowDOM 的元素是 Web 组件)具有生命周期阶段和回调。
    这个图:https : //andyogo.github.io/custom-element-reactions-diagram/必须要理解的。
    你想在 constructor 阶段中添加DOM内容;但是在这个阶段还没有 DOM 元素。
    只有在connectedCallbackDOM 内容才能被添加。
    使用shadowDOM 这是另一回事,它的“DocumentFragment”在 中可用 constructor ,您可以设置内容,但它还不是DOM 元素!在connectedCallback当您的自定义要素是附加到DOM告诉你。

  2. 模板内容是一个 DocumentFragment,但您.innerHTML需要一个字符串。
    由于(在您的使用中)<template> 一个 DOM 元素,您可以读取它的 innerHTML(见下文)


所以,是的,没有shadowDOM 的自定义元素是可能的:

您将看到两次<template>内容,演示了两种添加内容的方式。

<script>
  customElements.define("my-element", class extends HTMLElement {
    connectedCallback() {
      let template = document.getElementById(this.nodeName);
      this.innerHTML = template.innerHTML;
      this.append(template.content.cloneNode(true))
    }
  })

</script>

<template id="MY-ELEMENT">
  Hello, I am an Element!
</template>

<my-element></my-element>
Run Code Online (Sandbox Code Playgroud)


constructor 是您准备元素的地方

constructor 也会在您执行时运行document.createElement("my-element")

connectedCallback当你的元素被添加到DOM运行

如果你没有指定方法,它的父类中的方法就会运行,所以在上面的代码中 constructor ,HTMLElement 中的(默认)被执行。
这就是为什么您需要super()在自己的 constructor ... 中执行 constructor from HTMLElement。

笔记:

constructor(){
 let template = document.getElementById("MY-ELEMENT").content.cloneNode(true);
 super().attachShadow({mode:"open").append(template);
}
Run Code Online (Sandbox Code Playgroud)

是完全有效的代码;谷歌文档说“超级需要先运行”是错误的。
您需要先运行,super() 然后才能访问Elements 自己的作用域this

这就是为什么我更喜欢:

constructor(){

 // do anything you want here, but you can not use 'this'

 super() // returns 'this'
   .attachShadow({mode:"open") // both Sets AND Returns this.shadowRoot
   .append(document.getElementById(this.nodeName).content.cloneNode(true));
}
Run Code Online (Sandbox Code Playgroud)

当你的组件冒险将涉及到类继承时;
你调用父方法:

connectedCallback(){
  super.connectedCallback()
}
Run Code Online (Sandbox Code Playgroud)