Signatur
Beschreibung
Das Interface HTMLTemplateElement steht für das HTML-Element <template>. Dessen Inhalt wird vom Parser zwar eingelesen, aber nicht dargestellt und nicht ausgeführt: Bilder werden nicht geladen, Skripte laufen nicht, und Selektoren wie document.querySelector() finden die Kindelemente nicht. Der Inhalt liegt stattdessen in einem eigenen, inaktiven DocumentFragment, das über die Eigenschaft content erreichbar ist.
Templates eignen sich gut für wiederkehrende UI-Bausteine wie Listeneinträge, Tabellenzeilen, Karten oder Dialoge. Das übliche Vorgehen: den Inhalt mit template.content.cloneNode(true) oder document.importNode(template.content, true) klonen, im Klon die Daten eintragen (am besten über textContent) und den Klon anschließend ins DOM einfügen. Das ist sicherer und oft schneller als HTML-Strings über innerHTML zusammenzubauen.
Zusammen mit Web Components ist <template> das Standardmittel, um das Markup eines Shadow DOM festzulegen. Mit dem Attribut shadowrootmode (Eigenschaft shadowRootMode) kann der Parser sogar direkt einen deklarativen Shadow Root erzeugen, ganz ohne JavaScript.
Wichtige Eigenschaften:
content(nur lesbar):DocumentFragmentmit dem Inhalt des Templates.shadowRootMode: spiegelt das Attributshadowrootmodewider ("open"oder"closed").shadowRootDelegatesFocus,shadowRootClonable,shadowRootSerializable: weitere Optionen für deklarative Shadow Roots.
Rückgabewert
document.querySelector('template'), oder über document.createElement('template'). Einen eigenen Konstruktor gibt es nicht.Beispiele
Template anlegen und Inhalt auslesen
// Template per Skript anlegen (statt im HTML)
const tpl = document.createElement('template');
tpl.innerHTML = '<li class="item"><span class="name"></span></li>';
console.log(tpl instanceof HTMLTemplateElement);
console.log(tpl.content instanceof DocumentFragment);
console.log(tpl.childNodes.length); // Kinder hängen am Fragment, nicht am Template selbst
console.log(tpl.content.firstElementChild.className);
Liste aus Daten rendern (sicher per textContent)
// HTML:
// <template id="user-row">
// <tr><td class="name"></td><td class="mail"></td></tr>
// </template>
// <table><tbody id="users"></tbody></table>
const users = [
{ name: 'Anna', mail: 'anna@example.com' },
{ name: 'Ben', mail: 'ben@example.com' }
];
const template = document.querySelector('#user-row');
const tbody = document.querySelector('#users');
const fragment = document.createDocumentFragment();
for (const { name, mail } of users) {
const row = template.content.cloneNode(true);
row.querySelector('.name').textContent = name; // kein XSS-Risiko
row.querySelector('.mail').textContent = mail;
fragment.append(row);
}
tbody.append(fragment); // nur ein DOM-Einfügevorgang
console.log(tbody.rows.length);
Template als Shadow DOM einer Web Component
const tpl = document.createElement('template');
tpl.innerHTML = `
<style>:host { display: block; padding: .5rem; border: 1px solid #ccc; }</style>
<strong><slot name="title">Ohne Titel</slot></strong>
<p><slot></slot></p>
`;
class InfoCard extends HTMLElement {
constructor() {
super();
const root = this.attachShadow({ mode: 'open' });
root.append(tpl.content.cloneNode(true));
}
}
customElements.define('info-card', InfoCard);
const card = document.createElement('info-card');
card.innerHTML = '<span slot="title">Hinweis</span>Template-Inhalt im Shadow DOM.';
document.body.append(card);
console.log(card.shadowRoot.querySelector('slot').name);
Deklarativer Shadow Root (shadowRootMode)
// HTML, wird vom Parser direkt in einen Shadow Root verwandelt:
// <my-box>
// <template shadowrootmode="open"><p>Aus dem Template</p></template>
// </my-box>
const box = document.querySelector('my-box');
console.log(box.shadowRoot?.querySelector('p')?.textContent);
// Unterstützung prüfen
const supported = Object.hasOwn(HTMLTemplateElement.prototype, 'shadowRootMode');
console.log(supported);
// Wichtig · Fallstricke
- Immer klonen: Hängt man
template.contentdirekt an (parent.append(template.content)), werden die Knoten verschoben. Danach ist das Template leer. Deshalb vorhercloneNode(true)oderdocument.importNode(content, true)verwenden. - Kinder liegen im Fragment:
template.childrenist leer, unddocument.querySelector('#tpl .x')findet nichts. Stattdessentemplate.content.querySelector()benutzen. - Nach dem Einfügen ist der Inhalt aktiv: Enthaltene
<img>-,<script>- oder<iframe>-Elemente laden bzw. laufen, sobald der Klon im Dokument steckt. Wer unsichere Daten perinnerHTMLins Template schreibt, öffnet damit eine XSS-Lücke. Nutzerdaten gehören übertextContentodersetAttribute()in den Klon. - Skripte: Per
innerHTMLeingefügte<script>-Elemente werden grundsätzlich nicht ausgeführt. Aus einem Template geklonte Skripte laufen dagegen beim Einfügen, je nach Browser und Klonweg aber nicht immer zuverlässig. Darauf sollte man sich nicht verlassen. - Einsatz im Parser:
<template>ist fast überall erlaubt, auch in<table>,<select>und<head>. Dadurch lassen sich z. B.<tr>-Vorlagen gültig notieren. - Kompatibilität:
contentwird von allen modernen Browsern unterstützt, im Internet Explorer fehlt es. Deklaratives Shadow DOM (shadowRootMode) gibt es in Chrome ab 111, Safari ab 16.4 und Firefox ab 123.shadowRootClonableundshadowRootSerializablesind noch neuer. Bei Bedarf per Feature-Erkennung prüfen. - Wird
shadowrootmodenachträglich per JavaScript gesetzt oder das Template perinnerHTMLeingefügt, entsteht kein Shadow Root. Das klappt nur beim Parsen bzw. mitElement.setHTMLUnsafe()oderDocument.parseHTMLUnsafe().