Использование шаблонов может помочь разделить на модули и повторно использовать HTML-код на веб-сайте. Определяя шаблоны, инкапсулирующие часто используемый HTML-контент, мы можем уменьшить дублирование кода и упростить его обновление. Шаблон обычно используется вместе с пользовательскими элементами. Когда вы создаете пользовательский элемент с теневым DOM, теневой DOM создается динамически и существует как инкапсулированное дерево DOM внутри пользовательского элемента.

Тень ДОМ

Shadow DOM — это еще одна длинная история, я не буду вдаваться в подробности, вы можете обратиться к MDN Web Docs

«Теневой DOM позволяет присоединять скрытые деревья DOM к элементам в обычном дереве DOM — это дерево теневой DOM начинается с теневого корня, под которым вы можете прикрепить любой элемент так же, как и обычный DOM».

«Вы можете прикрепить теневой корень к любому элементу, используя метод Element.attachShadow(). В качестве параметра принимает объект параметров, который содержит один параметр — режим — со значением «открыто» или «закрыто».

Использование шаблона

Во-первых, давайте использовать шаблон для отображения в определенном формате.

Например, у нас может быть список изображений, полученных из API, которые мы хотим отобразить в определенном формате, мы можем определить шаблон и использовать JavaScript для заполнения шаблона данными и добавления его в DOM.

Мы получаем 10 случайных изображений из API с помощью запроса на выборку. Для каждого элемента в ответе мы клонируем item-template и устанавливаем src, textContent и добавляем экземпляр клонированного шаблона в контейнер.

<template id="item-template">
  <div class="item">
    <img src="" alt="Item image">
    <h2></h2>
    <p></p>
  </div>
</template>
const catTemplate = document.getElementById('item-template');
const catContainer = document.getElementById('container');
const apiKey = 'live_g2TlRXkZBAczbt1TkDyGIjgWk2Yd1wHzR7NahHoqdHGLa0bVho0uKmSl8u9otL1v';

fetch(`https://api.thecatapi.com/v1/images/search?limit=10`, {
  headers: {
    'x-api-key': apiKey
  }
})
  .then(response => response.json())
  .then(data => {
    data.forEach(cat => {
      const catInstance = catTemplate.content.cloneNode(true);
      catInstance.querySelector('img').src = cat.url;
      catInstance.querySelector('h2').textContent = cat.id;
      catInstance.querySelector('p').textContent = `Width: ${cat.width} Height: ${cat.height}`;
      catContainer.appendChild(catInstance);
    });
  })
  .catch(error => {
    console.error('Error fetching cat images:', error);
  });

Вложенный шаблон

Теперь мы заполнили контейнер 10 изображениями кошек, затем мы хотим добавить некоторые интерактивные функции к каждому изображению. Мы хотим добавить две кнопки на каждую картинку. Мы можем создать еще один шаблон и вложить его в шаблон элемента.

<template id="item-template">
  <div class="item">
    <img src="" alt="Item image">
    <h2></h2>
    <p></p>
    <button-group></button-group>
  </div>
</template>

<template id="button-template">
  <button class="share-btn">Share</button>
  <button class="favorite-btn">Favorite</button>
</template>

Использование шаблона с пользовательскими элементами

Шаблон и пользовательские элементы обычно появляются вместе.

Без определения пользовательского элемента (который здесь получен из шаблона с помощью document.getElementById(‘id-name’).content), шаблон не будет отображаться.

В этом примере мы определяем пользовательский элемент с именем button-group, который инкапсулирует шаблон кнопки. Затем мы добавляем элемент button-group в item-template, как указано выше, и используем его в цикле, который заполняет контейнер с элементами из API.

class ButtonGroup extends HTMLElement {
    constructor() {
      super();
      const template = document.getElementById('button-template');
      const shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(template.content.cloneNode(true));
      shadowRoot.querySelector('.share-btn').addEventListener('click', () => {
        console.log('Share button clicked');
      });
      shadowRoot.querySelector('.favorite-btn').addEventListener('click', () => {
        console.log('Favorite button clicked');
      });
    }
  }
  
customElements.define('button-group', ButtonGroup);

Использование шаблона с пользовательским элементом и слотом

Как мы упоминали выше, шаблон используется для определения структуры и стиля пользовательского элемента, но вы можете настроить содержимое пользовательского элемента без изменения самого шаблона. Вот здесь и пригодится элемент slot. Он служит заполнителем в шаблоне, позволяя передавать содержимое из основного документа в пользовательский элемент, эффективно корректируя данные в клонированном шаблоне.

<template id="button-template">
    <button class="share-btn">Share</button>
    <button class="favorite-btn">Favorite</button>
    <slot name="cat">
        <legend>meow!</legend>
    </slot>
</template>

В этом примере мы добавляем слот в button-template, после чего на картинке будет отображаться «мяу!»

Однако мы могли отредактировать «мяу!» без изменения шаблона кнопки, вместо этого мы могли бы изменить содержимое, которое имеет слот с тем же атрибутом имени, здесь он находится в шаблоне элемента, и мы меняем его на «мяу-мяу», тогда на картинке будет «мяу-мяу».

<template id="item-template">
    <div class="item">
        <img src="" alt="Item image">
        <h2></h2>
        <p></p>
        <button-group>
            <p slot="cat">meow meow</p>
        </button-group>
    </div>
</template>

Ссылка:

Сеть разработчиков Mozilla. (н.д.). Использование теневого DOM. Веб-документы MDN. https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM

веб.дев. (н.д.). Шаблон, слот и тень. веб.дев. https://web.dev/learn/html/template/

Репо работы:

https://github.com/Blurmilk/579-презентация

Развернутая страница:

https://blurmilk.github.io/579-presentation/