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 argumentotruesignifica 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.
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
insertButtonclona 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çãodisplayTemplateContentquando clicado. - A função
displayTemplateContentclona 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.contentdiretamente 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 dedocument.getElementByIdpara 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.