W3docs

Элемент Template

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

Элемент HTML <template> позволяет объявить фрагмент разметки на странице, который браузер разбирает, но не отображает. Содержимое остаётся в документе в инертном состоянии до тех пор, пока JavaScript не клонирует его и не вставит в живой DOM. Это делает <template> идиоматичным способом определить «шаблон» для повторяющихся элементов интерфейса — строк списков, карточек, модальных диалогов — без ручного создания узлов DOM или вставки HTML-строк в innerHTML.

В этой главе рассматривается, что делает содержимое шаблона особенным, как правильно клонировать и вставлять его, как привязывать события к клонированным узлам и какие подводные камни поджидают новичков. Шаблоны также являются строительным блоком Web Components, где они естественно сочетаются с пользовательскими элементами и Shadow DOM.

Понимание элемента Template

Элемент <template> служит контейнером для хранения HTML, который не отображается немедленно. Ключевая идея — инертность его содержимого:

  • Оно не отображается — разметка никогда не рисуется, вне зависимости от CSS.
  • Его ресурсы не загружаются<img>, <video> и <script> внутри шаблона не загружаются и не выполняются до тех пор, пока содержимое не будет клонировано в живой документ.
  • Скрипты внутри него не выполняются, а идентификаторы внутри него не конфликтуют с остальной частью страницы до момента активации.

Это принципиально отличается от скрытия элемента с помощью display: none. Элемент с display: none по-прежнему является частью живого DOM: его изображения загружаются, его скрипты выполняются, и document.getElementById находит элементы внутри него. Содержимое шаблона находится в отдельном дереве, основанном на фрагменте документа, поэтому ничего из этого не происходит до тех пор, пока вы явно не клонируете его.

<body>
  <div>You won't see the template, as it's not activated using JS.</div>
  <div id="template-container">
    <template id="my-template">
      <h1>Hidden!</h1>
    </template>
  </div>
</body>

Клонирование шаблонов для динамического контента

Шаблон — это лишь чертёж: сам по себе он не производит видимого вывода. Чтобы использовать его, нужно прочитать его свойство content (тип DocumentFragment), клонировать этот фрагмент, заполнить данными и добавить клон на страницу. Поскольку для каждого экземпляра вы создаёте клон, один шаблон может породить сколько угодно независимых копий, каждая со своими данными.

Существует два способа клонирования:

  • template.content.cloneNode(true) — клонирует фрагмент на месте. Аргумент true означает глубокое клонирование (включая всех потомков); без него копируется только пустой фрагмент.
  • document.importNode(template.content, true) — делает то же самое глубокое клонирование, но явно импортирует узлы в текущий документ. Это важно, когда шаблон находится в другом документе (например, содержимое, загруженное через <iframe> или <link rel="import">). Для шаблонов в том же документе оба способа эквивалентны; importNode является более безопасным вариантом по умолчанию.
Внимание

Всегда клонируйте содержимое шаблона перед вставкой. Если добавить template.content напрямую, исходные узлы перемещаются из шаблона в DOM — шаблон опустеет, и следующее клонирование не даст ничего. Клонирование сохраняет чертёж нетронутым для повторного использования.

<head>
  <style>
    .card {
      border: 1px solid #ccc;
      border-radius: 5px;
      padding: 10px;
      margin: 10px;
      width: 200px;
    }
    .card h3 {
      margin: 0;
    }
  </style>
</head>
<body>
  <div id="template-container">
    <!-- Template element -->
    <template id="card-template">
      <div class="card">
        <h3 id="card-title">Title</h3>
        <p id="card-content">Content goes here...</p>
      </div>
    </template>
  </div>

  <div id="card-container">
    <!-- Cards will be inserted here -->
  </div>

  <script>
    // Data for multiple cards
    const cardData = [
      { title: 'Card 1', content: 'This is the first card.' },
      { title: 'Card 2', content: 'This is the second card.' },
      { title: 'Card 3', content: 'This is the third card.' }
    ];

    // Function to create and insert cards
    function createCards(data) {
      const template = document.getElementById('card-template');

      data.forEach(item => {
        const clone = document.importNode(template.content, true);

        // Customize the cloned content
        clone.querySelector('#card-title').textContent = item.title;
        clone.querySelector('#card-content').textContent = item.content;

        // Insert the cloned content into the DOM
        document.getElementById('card-container').appendChild(clone);
      });
    }

    // Create cards with the provided data
    createCards(cardData);
  </script>
</body>

Добавление интерактивности с помощью JavaScript

Свойство content шаблона возвращает DocumentFragment, содержащий инертную разметку. Вы можете запрашивать и читать его (template.content.querySelector(...)), не затрагивая живую страницу, но более чистый подход — сначала клонировать, а затем привязывать события к клону — так каждый экземпляр получает собственные обработчики. Подробнее об обработчиках событий читайте в разделе Обработка событий в DOM.

Пример ниже определяет два шаблона: кнопку и карточку с содержимым. При нажатии на кнопку клонируется шаблон карточки и каждый раз добавляется свежая копия.

<body>
  <div id="template-container">
    <!-- Button Template -->
    <template id="button-template">
      <button id="show-content-btn">Add a content card</button>
    </template>

    <!-- Content Template -->
    <template id="content-template">
      <div class="content">
        <h2>Dynamic Content</h2>
        <p>This content is added dynamically when the button is clicked.</p>
      </div>
    </template>
  </div>

  <div id="button-container">
    <!-- Button will be inserted here -->
  </div>

  <div id="content-container">
    <!-- Content will be displayed here -->
  </div>

  <script>
    // Function to display template content
    function displayTemplateContent() {
      // Get the content template
      const contentTemplate = document.getElementById('content-template');
      // Access the .content property and clone it
      const contentClone = document.importNode(contentTemplate.content, true);

      // Display the cloned content
      document.getElementById('content-container').appendChild(contentClone);
    }

    // Insert the button template into the DOM
    function insertButton() {
      // Get the button template
      const buttonTemplate = document.getElementById('button-template');
      const buttonClone = document.importNode(buttonTemplate.content, true);

      // Add event listener to the button
      buttonClone.querySelector('#show-content-btn').addEventListener('click', displayTemplateContent);

      // Insert the button into the DOM
      document.getElementById('button-container').appendChild(buttonClone);
    }

    // Call the function to insert the button when the page loads
    insertButton();
  </script>
</body>

В этом примере:

  • У нас два шаблона: один для кнопки (button-template) и один для содержимого (content-template).
  • Функция insertButton клонирует шаблон кнопки и вставляет его в DOM. Также она прикрепляет обработчик события к кнопке, чтобы при нажатии вызывалась функция displayTemplateContent.
  • Функция displayTemplateContent клонирует шаблон содержимого и каждый раз при нажатии кнопки вставляет его в DOM.
  • Кнопка вставляется в DOM при загрузке страницы вызовом insertButton.

Таким образом, когда вы нажмёте кнопку «Попробуйте сами», вы увидите кнопку с надписью «Add a content card». При каждом нажатии на страницу добавляется новая карточка из шаблона содержимого, демонстрируя динамическую вставку, управляемую действиями пользователя.

Распространённые ошибки

Несколько типичных ловушек становятся причиной большинства ошибок при работе с шаблонами:

  • Забыли клонировать. Прямое добавление template.content опустошает шаблон. Всегда клонируйте сначала.
  • Запрос после добавления. appendChild(clone) перемещает узлы фрагмента в DOM, поэтому они становятся дочерними элементами целевого узла. Читайте и изменяйте клон (clone.querySelector(...)) до его добавления — после этого фрагмент пуст.
  • Дублирующиеся идентификаторы. Идентификаторы внутри шаблона нормальны в инертном состоянии, но после многократного клонирования каждая копия несёт тот же идентификатор — а дублирующиеся идентификаторы являются недопустимым HTML. Предпочитайте классы, атрибуты data-* или запросы на уровне клона вместо document.getElementById для поиска по экземплярам.
  • Ожидание выполнения скриптов. <script> внутри шаблона не выполняется при клонировании, если вы не пересоздадите его. Помещайте поведение в JavaScript, а не в разметку шаблона.

Поддержка браузерами

Элемент <template> является частью живого стандарта HTML и поддерживается во всех современных браузерах (Chrome, Firefox, Safari, Edge). Полифилл для современных браузеров не нужен, что делает его безопасным и независимым от сторонних библиотек решением для клиентских шаблонов.

Заключение

Элемент <template> предоставляет нативный способ без использования фреймворков определять повторно используемую разметку и создавать её экземпляры по требованию. Поскольку его содержимое инертно до клонирования, он исключает лишнюю отрисовку и загрузку ресурсов, характерную для скрытых элементов, а также обходит проблемы безопасности и разбора HTML-строк. Клонируйте с помощью cloneNode(true) или importNode, заполните клон данными, затем добавьте его — и вы получите эффективный паттерн, который масштабируется от одной карточки до целой системы компонентов. Чтобы увидеть шаблоны в рамках полноценного компонента, продолжайте с Пользовательскими элементами и Web Components.

Практика

Практика
Какова основная цель элемента Template в JavaScript?
Какова основная цель элемента Template в JavaScript?
Was this page helpful?