28 червня 2022 р.

Елемент template

Вбудований елемент <template> використовується як обгортка для шаблонної розмітки HTML. Браузер не бере до уваги його вміст, лише перевіряє на правильність синтаксису. Ми можемо доступитися та використовувати його у JavaScript, щоб створювати інші елементи.

Теоретично, ми могли б створити будь-який невидимий елемент де-небудь у HTML для зберігання у ньому розмітки HTML. Тоді що ж такого особливого у <template>?

По-перше, він може містити у собі будь-який коректний HTML, навіть якщо за звичайних умов він би потребував якогось додаткового тега.

Наприклад, ми можемо помістити тег рядка таблиці <tr>:

<template>
  <tr>
    <td>Вміст</td>
  </tr>
</template>

Зазвичай, якщо ми пробуємо помістити тег <tr> всередину, скажімо, тега <div>, браузер помічає некоректну структуру DOM та “виправляє” її, додаючи навколо <table>. Але це не те, що ми хочемо. На противагу цьому, <template> зберігає дані у тому вигляді, у якому ми їх туди помістили.

Ми також можемо помістити всередину <template> стилі чи скрипт:

<template>
  <style>
    p { font-weight: bold; }
  </style>
  <script>
    alert("Привіт");
  </script>
</template>

Браузер розглядає вміст тега <template> як такий, який існує “за межами документа”: до нього не застосовуються стилі, не виконуються скрипти, <video autoplay> не запускається і тому подібне.

Вміст починає опрацьовуватися (застосовуються стилі, виконуються скрипти і т. ін.), коли ми вставляємо його у документ.

Вставка шаблону

Вміст template доступний у його властивості content як DocumentFragment – спеціальний тип вузла DOM.

Ми можемо працювати з ним як зі всіма іншими вузлами DOM, за винятком однієї особливості: коли ми його кудись вставляємо, вставляються його дочірні елементи, а не він сам.

Наприклад:

<template id="tmpl">
  <script>
    alert("Привіт");
  </script>
  <div class="message">Привіт, світ!</div>
</template>

<script>
  let elem = document.createElement('div');

  // Клонувати вміст шаблону, для його подальшого використання
  elem.append(tmpl.content.cloneNode(true));

  document.body.append(elem);
  // Тепер запускається скрипт з <template>
</script>

Перепишемо приклад Shadow DOM з попереднього розділу, використовуючи <template>:

<template id="tmpl">
  <style> p { font-weight: bold; } </style>
  <p id="message"></p>
</template>

<div id="elem">Клікни мене</div>

<script>
  elem.onclick = function() {
    elem.attachShadow({mode: 'open'});

    elem.shadowRoot.append(tmpl.content.cloneNode(true)); // (*)

    elem.shadowRoot.getElementById('message').innerHTML = "Привіт зі світу тіней!";
  };
</script>

У рядку (*), коли ми клонуємо та вставляємо tmpl.content, як його DocumentFragment, натомість отримуємо його дочірні (<style>, <p>).

Вони формують Shadow DOM:

<div id="elem">
  #shadow-root
    <style> p { font-weight: bold; } </style>
    <p id="message"></p>
</div>

Підсумки

Узагальнимо:

  • <template> його вмістом може бути будь-який синтаксично правильний HTML.
  • <template> вміст вважається “за межами документа”, тому він ні на що не впливає.
  • Ми можемо доступитися до template.content з JavaScript, клонувати його, щоб потім знову використати у новому компоненті.

Тег <template> є унікальним, оскільки:

  • Браузер перевіряє HTML синтаксис всередині нього (на відміну від рядка всередині скрипта з тією ж розміткою).
  • …Але все одно дозволяє використання будь-якого тега HTML, навіть тих, використання яких не має сенсу без відповідної обгортки (напр. <tr>).
  • Вміст стає інтерактивним: виконуються скрипти, запускається <video autoplay> і т. ін., коли ми переміщаємо його у документ.

Елемент <template> не має жодних механізмів ітерації, зв’язування даних чи заміни змінних, але ми можемо реалізувати їх поверх нього.

Навчальна карта