3

当我尝试将模板附加到影子 DOM 时,它仅显示为“#documentFragment”,并且从不呈现或复制模板中结构的实际元素。

我花了几个小时试图弄清楚。我找到的解决方案是使用:

  • template.firstElementChild.cloneNode(true);

代替:

  • 模板.content.cloneNode(true);

然后,只有这样,一切都会按预期工作。

我的问题是,我做错了吗?

const template = document.createElement('template');
const form = document.createElement('form');
const gateway = document.createElement('fieldset');
const legend = document.createElement('legend');
gateway.appendChild(legend);
const username = document.createElement('input');
username.setAttribute('type', 'email');
username.setAttribute('name', 'username');
username.setAttribute('placeholder', 'email@address.com');
username.setAttribute('id', 'username');
gateway.appendChild(username);
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.innerHTML = 'Next';
gateway.appendChild(button);
form.appendChild(gateway);
template.appendChild(form);
class UserAccount extends HTMLElement {
  constructor() {
    super();
    const shadowDOM = this.attachShadow({
      mode: 'open'
    });
    const clone = template.firstElementChild.cloneNode(true);
    // This does not work
    // const clone = template.content.cloneNode(true);
    shadowDOM.appendChild(clone);
    shadowDOM.querySelector('legend').innerHTML = this.getAttribute('api');
  }
}
window.customElements.define('user-account', UserAccount);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

  <!-- <link rel="stylesheet" href="./css/main.css"> -->
  <script src="./js/user-account.js" defer></script>
  <title>Title</title>
</head>

<body>

  <user-account api="/accounts"></user-account>

</body>

</html>

4

2 回答 2

3

TEMPLATES仅当您需要制作多个副本或希望尽可能使用纯 HTML + CSS 时才有意义。

许多 Web 组件显示了用法:

const template = document.createElement("template");
template.innerHTML = "Hello World"

然后做:

constructor() {
    super();
    this._shadowRoot = this.attachShadow({ mode: "open" });
    this._shadowRoot.appendChild(template.content.cloneNode(true));
  }

其中,因为模板仅用作单个“父”容器,您可以写成:

constructor() {
    super().attachShadow({ mode: "open" }).innerHTML = "Hello World";
  }

注意:super()returnsthisattachShadow() setsreturn this.shadowRoot ... 免费

两种类型的模板

您可以<TEMPLATE>DOM中创建一个,也可以template内存中创建一个

内存中的模板

10 个内存模板中有 9 个可以使用其他 HTMLElements 作为容器来完成,
就像您的代码一样,其中FORM可以是主容器。不需要template容器。

如果您确实在内存中构建了模板,请了解append()over 的值(经常被误用)appendChild()

In Memory 模板非常适合进行(许多)更改(使用代码)

DOM 中的模板

无需尝试在 JavaScript 字符串中填充HTMLCSS ,您在 HTML 文档中有一个 DOM!
使用<TEMPLATE>HTML 元素。

将 shadowDOM 添加<slot>到组合中,您将花费更少的时间来调试 JavaScript,而将更多的时间用于编写语义 HTML。

DOM 模板非常适合对更多静态 HTML/CSS 结构进行简单的 HTML 和 CSS 编辑(在您的 IDE 中使用语法高亮显示)


这是您的代码的两种类型的模板,哪一种对开发人员来说更容易?

  const form = document.createElement('form');
  const gateway = document.createElement('fieldset');
  const legend = document.createElement('legend');
  const username = document.createElement('input');
  username.setAttribute('type', 'email');
  username.setAttribute('name', 'username');
  username.setAttribute('placeholder', 'email@address.com');
  username.setAttribute('id', 'username');
  const button = document.createElement('button');
  button.setAttribute('type', 'button');
  button.innerHTML = 'Next';
  gateway.append(legend,username,button);
  form.appendChild(gateway);
  
  class Form extends HTMLElement {
    constructor(element) {
      super().attachShadow({mode:'open'}).append(element);
    }
    connectedCallback() {
      this.shadowRoot.querySelector('legend').innerHTML = this.getAttribute('api');
    }
  }
  
  window.customElements.define('form-one', class extends Form {
    constructor() {
      super(form)
    }
  });
  window.customElements.define('form-two', class extends Form {
    constructor() {
      super(document.getElementById("FormTwo").content);
    }
  });
<template id="FormTwo">
  <form>
    <fieldset>
      <legend></legend>
      <input type="email" name="username" placeholder="email@address.com" id="username">
      <button type="button">Next</button>
    </fieldset>
  </form>
</template>

<form-one api="/accounts"></form-one>
<form-two api="/accounts"></form-two>

笔记:

在上面的代码中,<TEMPLATE>.content移动到了 shadowDOM。

要重用(克隆)<TEMPLATE>代码必须是:

super(document.getElementById("FormTwo").content.cloneNode(true));

为什么你template.content失败了

您的代码失败,因为

  const template = document.createElement('template');
  const form = document.createElement("form");
  template.appendChild(form);

template没有内容_

TEMPLATE不是常规的 HTMLElement,您必须附加到.content

  const template = document.createElement('template');
  const form = document.createElement("form");
  template.content.appendChild(form);

将工作

大多数 Web 组件示例显示:

  const template = document.createElement("template");
  template.innerHTML = "Hello World"

innerHTML设置.content 在引擎盖下

这解释了为什么而不是:

template.content.appendChild(form);

你可以写:

template.innerHTML = form.outerHTML;

于 2020-08-05T08:59:37.140 回答
1

“模板”元素是一种特殊元素,实际上不会立即渲染(参考)。这就是为什么附加模板不会产生任何结果。

template.firstElementChild.cloneNode意思是“获取模板的子项(即表单)并克隆它”,这与仅附加表单相同,可以正常工作(如下)。

const template = document.createElement('template');
const form = document.createElement('form');
const gateway = document.createElement('fieldset');
const legend = document.createElement('legend');
gateway.appendChild(legend);
const username = document.createElement('input');
username.setAttribute('type', 'email');
username.setAttribute('name', 'username');
username.setAttribute('placeholder', 'email@address.com');
username.setAttribute('id', 'username');
gateway.appendChild(username);
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.innerHTML = 'Next';
gateway.appendChild(button);
form.appendChild(gateway);
template.appendChild(form);
class UserAccount extends HTMLElement {
  constructor() {
    super();
    const shadowDOM = this.attachShadow({
      mode: 'open'
    });
    shadowDOM.appendChild(form);
    shadowDOM.querySelector('legend').innerHTML = this.getAttribute('api');
  }
}

window.customElements.define('user-account', UserAccount);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

  <!-- <link rel="stylesheet" href="./css/main.css"> -->
  <script src="./js/user-account.js" defer></script>
  <title>Title</title>
</head>

<body>

  <user-account api="/accounts"></user-account>

</body>

</html>

于 2020-08-05T01:27:12.220 回答