10 июля 2019 г.

Элемент "template"

Встроенный элемент <template> предназначен для хранения шаблона HTML. Браузер полностью игнорирует его содержимое, проверяя лишь синтаксис, но мы можем использовать этот элемент в JavaScript, чтобы создать другие элементы.

В теории, для хранения разметки мы могли бы создать невидимый элемент в любом месте 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>).

Именно они и формируют теневой 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> не поддерживает итерацию, связывания данных или подстановки переменных. Однако эти возможности можно реализовать поверх него.

Карта учебника