W3docs

Elemento Template

Aprenda como o elemento HTML <template> armazena marcação inerte e reutilizável e como clonar seu conteúdo com JavaScript para construir interfaces dinâmicas de forma eficiente.

O elemento HTML <template> permite declarar um bloco de marcação na sua página que o navegador analisa, mas não renderiza. O conteúdo fica pronto no documento, inerte, até que seu JavaScript o clone e o insira no DOM ativo. Isso torna o <template> a forma idiomática de definir um "modelo" para elementos de interface repetidos — linhas de listas, cards, diálogos modais — sem construir nós do DOM manualmente ou colar strings HTML no innerHTML.

Este capítulo aborda o que torna o conteúdo de um template especial, como cloná-lo e inseri-lo corretamente, como vincular eventos em nós clonados e as armadilhas que atingem usuários iniciantes. Os templates também são um componente fundamental dos Web Components, onde se combinam naturalmente com elementos personalizados e o Shadow DOM.

Entendendo o Elemento Template

O elemento <template> funciona como um contêiner para armazenar HTML que não é renderizado imediatamente. O que torna seu conteúdo inerte é a ideia central:

  • Ele não é exibido — a marcação nunca é pintada, independentemente do CSS.
  • Seus recursos não são buscados<img>, <video> e <script> dentro de um template não carregam nem executam até que o conteúdo seja clonado no documento ativo.
  • Scripts dentro dele não executam, e IDs dentro dele não colidem com o restante da página até serem ativados.

Isso é fundamentalmente diferente de ocultar um elemento com display: none. Um elemento com display: none ainda faz parte do DOM ativo: suas imagens carregam, seus scripts executam e document.getElementById encontra elementos dentro dele. O conteúdo de um template vive em uma árvore separada com suporte de um fragmento de documento, portanto, nada disso acontece até que você opte por cloná-lo.

<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>

Clonando Templates para Conteúdo Dinâmico

Um template é apenas um modelo — por si só não produz saída visível. Para utilizá-lo, você lê sua propriedade content (um DocumentFragment), clona esse fragmento, preenche seus dados e anexa o clone à página. Como você clona para cada instância, um único template pode produzir quantas cópias independentes forem necessárias, cada uma com seus próprios dados.

Há duas formas de clonar:

  • template.content.cloneNode(true) — clona o fragmento no lugar. O argumento true significa um clone profundo (incluindo todos os descendentes); sem ele, você copiaria apenas o fragmento vazio.
  • document.importNode(template.content, true) — realiza o mesmo clone profundo, mas importa explicitamente os nós para o documento atual. Isso importa quando o template vive em um documento diferente (por exemplo, conteúdo obtido via <iframe> ou <link rel="import">). Para templates no mesmo documento, os dois são equivalentes; importNode é o padrão mais seguro.
Aviso

Sempre clone o conteúdo do template antes de inseri-lo. Se você anexar template.content diretamente, você move os nós originais para fora do template para o DOM — o template ficará vazio e o próximo clone não produzirá nada. Clonar mantém o modelo intacto para reutilização.

<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>

Aprimorando a Interatividade com JavaScript

A propriedade content de um template retorna um DocumentFragment que contém a marcação inerte. Você pode consultá-la e lê-la (template.content.querySelector(...)) sem tocar na página ativa, mas um padrão mais limpo é clonar primeiro e depois vincular eventos ao clone — dessa forma, cada instância obtém seus próprios ouvintes. Para uma cobertura mais aprofundada sobre como anexar manipuladores, consulte Manipulação de Eventos no DOM.

O exemplo abaixo define dois templates: um botão e um card de conteúdo. Clicar no botão clona o template do card e anexa uma cópia nova a cada vez.

<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>

Neste exemplo:

  • Temos dois templates: um para um botão (button-template) e um para conteúdo (content-template).
  • A função insertButton clona o template do botão e o insere no DOM. Ela também anexa um ouvinte de evento ao botão para chamar a função displayTemplateContent quando clicado.
  • A função displayTemplateContent clona o template de conteúdo e o insere no DOM cada vez que o botão é clicado.
  • O botão é inserido no DOM quando a página carrega, chamando insertButton.

Assim, ao clicar no botão "Try it yourself", você verá um botão com o rótulo "Add a content card". Cada vez que você clicar nele, um novo card do template de conteúdo é adicionado à página, demonstrando a inserção dinâmica orientada pela interação do usuário.

Armadilhas Comuns

Algumas ciladas são responsáveis pela maioria dos bugs com templates:

  • Esquecer de clonar. Anexar template.content diretamente esvazia o template. Sempre clone primeiro.
  • Consultar após anexar. appendChild(clone) esvazia o fragmento no DOM, fazendo com que os nós do clone se tornem filhos do alvo. Leia ou modifique o clone (clone.querySelector(...)) antes de anexá-lo — depois, o fragmento estará vazio.
  • IDs duplicados. IDs escritos dentro de um template são válidos enquanto inertes, mas ao cloná-lo muitas vezes, cada cópia carrega o mesmo ID — e IDs duplicados são HTML inválido. Prefira classes, atributos data-* ou consultas com escopo no clone em vez de document.getElementById para buscas por instância.
  • Esperar que scripts executem. Um <script> dentro de um template não executa ao ser clonado, a menos que você o recrie. Mantenha o comportamento no seu JavaScript, não na marcação do template.

Suporte a Navegadores

O elemento <template> faz parte do HTML Living Standard e é suportado em todos os navegadores modernos (Chrome, Firefox, Safari, Edge). Nenhum polyfill é necessário para os navegadores atuais, o que o torna uma escolha segura e sem dependências para templates no lado do cliente.

Conclusão

O elemento <template> oferece uma forma nativa, independente de framework, de definir marcação reutilizável e instanciá-la sob demanda. Como seu conteúdo é inerte até ser clonado, ele evita a renderização e o carregamento de recursos desnecessários de elementos ocultos, e contorna as peculiaridades de segurança e análise de construir HTML a partir de strings. Clone com cloneNode(true) ou importNode, preencha o clone e anexe — e você terá um padrão eficiente que escala de um único card para um sistema completo de componentes. Para ver templates dentro de um componente completo, continue com Elementos Personalizados e Web Components.

Prática

Prática
Qual é o objetivo principal do Elemento Template em JavaScript?
Qual é o objetivo principal do Elemento Template em JavaScript?
Was this page helpful?