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