Как добавить элемент ШАБЛОН в теневой дом? - PullRequest
1 голос
/ 05 августа 2020

Когда я пытаюсь добавить шаблон в теневой DOM, он отображается только как "#documentFragment" и никогда не отображает и не копирует фактические элементы, структурированные в шаблоне. вне. Решение, которое я нашел, заключалось в использовании:

  • template.firstElementChild.cloneNode (true);

вместо:

  • template.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>

Ответы [ 2 ]

1 голос
/ 05 августа 2020

TEMPLATES интересны только в том случае, если вам нужно сделать несколько копий или вы хотите работать в простом HTML + CSS как можно больше.

Многие веб-компоненты показывают использование:

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() возвращает this, а attachShadow() устанавливает и возвращает this.shadowRoot ... бесплатно

Два типа ШАБЛОНОВ

Вы можете создать <TEMPLATE> в DOM , или вы можете создать template в Память

Шаблоны в памяти

9 из 10 Шаблоны памяти могут быть выполнены с другими HTMLElements в качестве контейнера, как в случае с вашим кодом, где FORM может быть основным контейнером. Нет необходимости в контейнере template.

Если вы создаете шаблон в памяти, узнайте значение append() сверх (часто используется неправильно) appendChild()

Шаблоны в памяти отлично подходят для внесения (многих) изменений (с кодом)

Шаблоны в DOM

Не нужно пытаться набивать HTML и CSS в строках JavaScript, у вас есть DOM в документе HTML! Используйте элемент <TEMPLATE> HTML.

Добавьте shadowDOM <slot> в микс, и вы потратите меньше времени на отладку JavaScript и больше времени на написание semanti c HTML.

Шаблоны DOM отлично подходят для простого HTML и CSS редактирования (в вашей среде IDE с выделением синтаксиса) большего количества c HTML / CSS структур


Вот оба типа ШАБЛОНОВ с вашим кодом, какой из них проще для разработчика?

  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);

будет работать

Большинство примеров веб-компонентов показывают:

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

innerHTML наборы .content под капотом

Это объясняет, почему вместо:

template.content.appendChild(form);

вы можете написать:

template.innerHTML = form.outerHTML;

1 голос
/ 05 августа 2020

Элемент 'шаблон' - это специальный элемент, который фактически не отображается сразу ( ссылка ). Вот почему добавление шаблона ничего не дает.

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>
...