如何在不使用attachShadow的情况下创建自定义元素?

问题描述 投票:0回答:2

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

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

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

<my-elem></my-elem>

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

this.append(templateContent.cloneNode(true)); 

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

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

this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true));

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

javascript html web-component shadow-dom
2个回答
13
投票

你正陷入多个陷阱,就像每个人在第一次组件冒险中一样。

  1. 自定义元素(严格来说,只有 withshadowDOM 元素才是 Web 组件)具有生命周期阶段和回调。
    此图:https://andyogo.github.io/custom-element-reactions-diagram/必须理解的。
    你想在

    constructor
    阶段添加 DOM 内容;但此阶段还没有 DOM 元素。
    只有在
    connectedCallback
    中才能添加DOM内容。
    对于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> <!-- the TEMPLATE MUST exist in the DOM for <my-element> to use it! --> <my-element></my-element>


constructor

是您
准备您的元素的地方

当您执行

constructor

 时,此 
document.createElement("my-element")
 也会运行。

当您的元素添加到 DOM 时,

connectedCallback

 就会运行

如果不指定方法,则运行其父类中的方法,因此在上面的代码中,执行 HTMLElement 中的(默认)

constructor

这就是为什么您需要在自己的
super()
 中使用 
constructor
...从 HTMLElement 执行 
constructor

注:

constructor(){ let template = document.getElementById("MY-ELEMENT").content.cloneNode(true); super().attachShadow({mode:"open").append(template); }
是完全有效的代码;谷歌文档说 

“super 需要先运行” 是错误的。 您需要运行
super()

 
before 您可以使用 this
访问
Elements 自己的范围

这就是为什么我更喜欢:

constructor(){ // do anything you want here, but you can not use 'this' super() // Sets AND Returns 'this' .attachShadow({mode:"open") // both Sets AND Returns this.shadowRoot .innerHTML = ` ... `; }

constructor(){ // do anything you want here, but you can not use 'this' super() // Sets AND Returns 'this' .attachShadow({mode:"open") // both Sets AND Returns this.shadowRoot .append(document.getElementById(this.nodeName).content.cloneNode(true)); }
注意 

append() 在 IE 中不可用;所以 oldskool 程序员不会知道它的多功能性:https://developer.mozilla.org/en-US/docs/Web/API/Element/append

当您的组件冒险将涉及类继承时;

您可以使用以下方式调用父方法:

connectedCallback(){ super.connectedCallback() }
    

3
投票
自定义元素最简单的实现是:

class MyComponent extends HTMLElement { connectedCallback() { this.innerHTML = `<div>Hello world</div>` } } customElements.define('my-component', MyComponent)
my-component {
  display: block;
  border: 1px dotted #900
}
<my-component></my-component>

但是,如果不使用 Shadow DOM,则无法封装 CSS,而必须通过外部样式表来设置组件的样式。

使用 Shadow DOM 编写组件的最简单方法如下所示:

class MyOtherComponent extends HTMLElement { constructor() { super() this.shadow = this.attachShadow({ mode: "open" }) } connectedCallback() { this.shadow.innerHTML = ` <style> :host { display: block; border: 1px dotted #900 } </style> <div class="component">Hello World!</div> ` } } customElements.define('my-other-component', MyOtherComponent)
<my-other-component></my-other-component>

这样,你的开销会多一些,但组件确实被封装了。

© www.soinside.com 2019 - 2024. All rights reserved.