L'élément Template
Apprenez comment l'élément HTML <template> stocke un balisage inerte et réutilisable, et comment cloner son contenu avec JavaScript pour créer des interfaces dynamiques.
L'élément HTML <template> vous permet de déclarer un fragment de balisage dans votre page que le navigateur analyse mais ne pas affiche. Le contenu reste disponible dans le document, inerte, jusqu'à ce que votre JavaScript le clone et l'insère dans le DOM actif. Cela fait de <template> la manière idiomatique de définir un « modèle » pour une interface répétée — lignes de liste, cartes, boîtes de dialogue modales — sans construire de nœuds DOM manuellement ni coller des chaînes HTML dans innerHTML.
Ce chapitre couvre ce qui rend le contenu des templates spécial, comment le cloner et l'insérer correctement, comment câbler des événements sur les nœuds clonés, ainsi que les pièges qui guettent les nouveaux utilisateurs. Les templates sont également un composant de base des Web Components, où ils s'associent naturellement aux éléments personnalisés et au Shadow DOM.
Comprendre l'élément Template
L'élément <template> agit comme un conteneur pour stocker du HTML qui n'est pas immédiatement rendu. Ce qui rend son contenu inerte est l'idée clé :
- Il n'est pas affiché — le balisage n'est jamais peint, quel que soit le CSS.
- Ses ressources ne sont pas récupérées —
<img>,<video>et<script>à l'intérieur d'un template ne se chargent ni ne s'exécutent tant que le contenu n'est pas cloné dans le document actif. - Les scripts qu'il contient ne s'exécutent pas, et les IDs à l'intérieur n'entrent pas en conflit avec le reste de la page tant qu'ils ne sont pas activés.
C'est fondamentalement différent de masquer un élément avec display: none. Un élément avec display: none fait toujours partie du DOM actif : ses images se chargent, ses scripts s'exécutent, et document.getElementById trouve les éléments à l'intérieur. Le contenu d'un template vit dans un arbre séparé, soutenu par un fragment de document, donc rien de tout cela ne se produit jusqu'à ce que vous optiez pour le cloner.
<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>Cloner les Templates pour du Contenu Dynamique
Un template n'est qu'un plan — il ne produit aucune sortie visible par lui-même. Pour l'utiliser, vous lisez sa propriété content (un DocumentFragment), clonez ce fragment, remplissez vos données, et ajoutez le clone à la page. Comme vous clonez pour chaque instance, un seul template peut produire autant de copies indépendantes que nécessaire, chacune avec ses propres données.
Il y a deux façons de cloner :
template.content.cloneNode(true)— clone le fragment sur place. L'argumenttruesignifie un clone profond (inclure tous les descendants) ; sans lui, vous ne copierez que le fragment vide.document.importNode(template.content, true)— effectue le même clone profond mais importe explicitement les nœuds dans le document courant. Cela est important lorsque le template réside dans un document différent (par exemple, du contenu importé via une<iframe>ou<link rel="import">). Pour les templates dans le même document, les deux sont équivalents ;importNodeest la valeur par défaut la plus sûre.
Clonez toujours le contenu du template avant de l'insérer. Si vous ajoutez template.content directement, vous déplacez les nœuds originaux hors du template dans le DOM — le template est alors vide et le prochain clone ne produit rien. Le clonage laisse le plan intact pour une réutilisation.
<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>Améliorer l'Interactivité avec JavaScript
La propriété content d'un template retourne un DocumentFragment qui contient le balisage inerte. Vous pouvez l'interroger et le lire (template.content.querySelector(...)) sans toucher à la page active, mais le modèle le plus propre consiste à cloner d'abord, puis à câbler les événements sur le clone — de cette façon, chaque instance obtient ses propres écouteurs. Pour une couverture plus approfondie de l'attachement de gestionnaires, consultez La gestion des événements dans le DOM.
L'exemple ci-dessous définit deux templates : un bouton et une carte de contenu. Cliquer sur le bouton clone le template de la carte et ajoute une nouvelle copie à chaque fois.
<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>Dans cet exemple :
- Nous avons deux templates : un pour un bouton (
button-template) et un pour le contenu (content-template). - La fonction
insertButtonclone le template du bouton et l'insère dans le DOM. Elle attache également un écouteur d'événement au bouton pour appeler la fonctiondisplayTemplateContentlors d'un clic. - La fonction
displayTemplateContentclone le template de contenu et l'insère dans le DOM à chaque clic sur le bouton. - Le bouton est inséré dans le DOM au chargement de la page en appelant
insertButton.
Ainsi, lorsque vous cliquez sur le bouton « Try it yourself », vous verrez un bouton intitulé « Add a content card ». Chaque fois que vous cliquez dessus, une nouvelle carte provenant du template de contenu est ajoutée à la page, démontrant une insertion dynamique pilotée par l'interaction utilisateur.
Pièges Courants
Quelques écueils sont à l'origine de la plupart des bogues liés aux templates :
- Oublier de cloner. Ajouter
template.contentdirectement vide le template. Clonez toujours en premier. - Interroger après l'ajout.
appendChild(clone)vide le fragment dans le DOM, donc les nœuds du clone deviennent des enfants de la cible. Lisez ou modifiez le clone (clone.querySelector(...)) avant de l'ajouter — après, le fragment est vide. - IDs en double. Les IDs écrits à l'intérieur d'un template sont corrects tant qu'ils sont inertes, mais une fois que vous le clonez plusieurs fois, chaque copie porte le même ID — et les IDs en double constituent du HTML invalide. Préférez les classes, les attributs
data-*, ou des requêtes scoped sur le clone plutôt quedocument.getElementByIdpour les recherches par instance. - S'attendre à ce que les scripts s'exécutent. Un
<script>à l'intérieur d'un template ne s'exécute pas lors du clonage, sauf si vous le recréez. Gardez le comportement dans votre JavaScript, pas dans le balisage du template.
Support des Navigateurs
L'élément <template> fait partie du standard HTML Living Standard et est pris en charge par tous les navigateurs modernes (Chrome, Firefox, Safari, Edge). Aucun polyfill n'est nécessaire pour les navigateurs actuels, ce qui en fait un choix sûr et sans dépendance pour le templating côté client.
Conclusion
L'élément <template> vous offre un moyen natif, sans framework, de définir un balisage réutilisable et de l'instancier à la demande. Parce que son contenu est inerte jusqu'au clonage, il évite le rendu inutile et le chargement de ressources des éléments cachés, et contourne les particularités de sécurité et d'analyse liées à la construction de HTML à partir de chaînes. Clonez avec cloneNode(true) ou importNode, peuplez le clone, puis ajoutez-le — et vous disposez d'un modèle efficace qui passe d'une seule carte à un système de composants entier. Pour voir les templates dans un composant complet, continuez avec les Éléments personnalisés et les Web Components.