W3docs

Fortgeschrittene DOM-Techniken

Fortgeschrittene DOM-Techniken meistern: Templates und Shadow DOM für modularen, wartbaren JavaScript-Code ohne Framework einsetzen.

Das Beherrschen fortgeschrittener DOM-Techniken hilft Ihnen dabei, dynamische, modulare und wartbare Oberflächen mit reinem JavaScript zu erstellen — ganz ohne Framework. Dieser Leitfaden behandelt zwei Säulen moderner Komponentenentwicklung: das <template>-Element zur Definition wiederverwendbarer, inaktiver Markup-Strukturen sowie das Shadow DOM zur Kapselung der Struktur, der Stile und des Verhaltens einer Komponente. Zusammen bilden sie das Fundament, auf dem Web Components und Custom Elements aufbauen.

Templates erstellen und verwenden

Wozu Templates dienen

Vor dem <template>-Element bauten Entwickler wiederverwendbares Markup, indem sie HTML in versteckte <div>-Elemente, JavaScript-Zeichenkettenliterale oder <script type="text/template">-Blöcke steckten. Jeder Ansatz hat einen Nachteil: Versteckte <div>s verursachen immer noch Browser-Parsing- und Ressourcenladekosten (Bilder werden geladen, Skripte ausgeführt), und String-Templates verlieren die Syntaxhervorhebung und sind leicht zu beschädigen.

Das <template>-Element löst dieses Problem. Seine Inhalte werden geparst, sind aber inaktiv: Der Browser erstellt die DOM-Knoten, rendert sie jedoch nicht, führt ihre Skripte nicht aus und lädt ihre Bilder oder Medien nicht, bis Sie den Inhalt explizit in das aktive Dokument klonen. Dadurch ist <template> das richtige Werkzeug zum Deklarieren von Markup, das Sie viele Male instanziieren möchten.

Das <template>-Element verwenden

Das <template>-Element ermöglicht es Ihnen, HTML zu definieren, das beim Laden der Seite nicht gerendert wird. Sie greifen auf seinen Inhalt über die schreibgeschützte content-Eigenschaft zu, die ein DocumentFragment zurückgibt.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Using the <template> Element</title>
</head>
<body>
    <template id="my-template">
        <div class="card">
            <h2>Title</h2>
            <p>Content goes here...</p>
        </div>
    </template>
    <button id="show-template">Show Template</button>
    <div id="content"></div>

    <script>
        document.getElementById('show-template').addEventListener('click', () => {
            const template = document.getElementById('my-template');
            const content = document.getElementById('content');
            const clone = template.content.cloneNode(true);
            content.appendChild(clone);
        });
    </script>
</body>
</html>

Dieses Beispiel zeigt die grundlegende Struktur eines <template>-Elements, das eine Karte mit einem Titel und Inhalt enthält. Der Inhalt des Templates wird geklont und in den DOM eingefügt, wenn die Schaltfläche angeklickt wird. Für eine ausführlichere Betrachtung des Elements an sich, lesen Sie das <template>-Element.

Template-Inhalt klonen und einfügen

Um ein <template> wiederzuverwenden, klonen Sie seinen content und fügen den Klon in den DOM ein. Übergeben Sie true an cloneNode, damit der gesamte Teilbaum (das Element und alle seine Nachkommen) kopiert wird — cloneNode(false) kopiert nur den obersten Knoten und würde ein leeres Fragment liefern.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Cloning and Inserting Template Content</title>
</head>
<body>
    <template id="card-template">
        <div class="card">
            <h2 class="title"></h2>
            <p class="body"></p>
        </div>
    </template>

    <button id="add-card">Add Card</button>
    <div id="container"></div>

    <script>
        let count = 0;
        document.getElementById('add-card').addEventListener('click', () => {
            const template = document.getElementById('card-template');
            const clone = template.content.cloneNode(true);

            // Fill the clone with dynamic data before inserting it.
            count++;
            clone.querySelector('.title').textContent = 'Card ' + count;
            clone.querySelector('.body').textContent = 'Created at ' + new Date().toLocaleTimeString();

            document.getElementById('container').appendChild(clone);
        });
    </script>
</body>
</html>

Der eigentliche Mehrwert von Templates liegt in diesem Muster: klonen, dann den Klon mit Daten befüllen, bevor er eingefügt wird. Einige Details, die es wert sind, beachtet zu werden:

  • Ein DocumentFragment leert sich beim Anhängen selbst. Nach appendChild(clone) wandern die Kinder des Fragments in den Container, und das Fragment bleibt leer — rufen Sie cloneNode also einmal pro Element auf, das Sie hinzufügen möchten.
  • Abfragen Sie den Klon, nicht das Dokument. Selektoren wie clone.querySelector('.title') operieren auf dem noch nicht eingefügten Fragment, sodass Sie es befüllen, bevor es jemals den aktiven DOM erreicht (was zusätzliche Reflows vermeidet). Siehe Suchen mit querySelector.
  • document.importNode(template.content, true) ist das dokumentübergreifende Äquivalent — verwenden Sie es, wenn das Template in einem anderen Dokument oder iframe lebt, damit die importierten Knoten dem aktuellen Dokument gehören.

Shadow DOM

Einführung in das Shadow DOM

Das Shadow DOM ist ein Webstandard, der die Kapselung in Web Components ermöglicht. Es hängt einen separaten, versteckten DOM-Baum — den Shadow Tree — an ein Element (den Shadow Host). Knoten innerhalb dieses Baums sind über das normale document.querySelector der Seite nicht erreichbar, und innerhalb definierte Stile lecken nicht in den Rest der Seite. Dies hält die interne Struktur, die Stile und das Verhalten einer Komponente vom globalen Dokument isoliert.

Einige Begriffe, die Sie häufig begegnen werden:

  • Shadow Host — das reguläre Element, an das der Shadow Tree angehängt ist.
  • Shadow Root — der Wurzelknoten des Shadow Trees, zurückgegeben von attachShadow().
  • Shadow Boundary — die Grenze zwischen dem Shadow Tree und dem Rest des Dokuments, die das Scoping nicht überschreitet.

Offener vs. geschlossener Modus

attachShadow() erfordert eine mode-Option:

const open = host.attachShadow({ mode: 'open' });
// host.shadowRoot  →  the shadow root (accessible from outside)

const closed = host2.attachShadow({ mode: 'closed' });
// host2.shadowRoot →  null (the root is hidden from outside scripts)

In der Praxis bevorzugen Sie open. Der closed-Modus bietet keine echte Sicherheit — jeder kann attachShadow überschreiben, bevor Ihr Code ausgeführt wird — und er macht Ihre Komponente nur schwerer zu testen und zu debuggen.

Kapselung und komponentenbasierte Entwicklung

Die Kapselung stellt sicher, dass die innerhalb einer Komponente definierten Stile und Skripte nicht herauslecken und den Rest des Dokuments beeinflussen — und dass äußere Stile nicht eindringen. Das folgende Beispiel hängt eine Shadow Root an und erstellt dann deren Inhalt in einem DocumentFragment, damit der gesamte Teilbaum in einer einzigen Operation eingefügt wird. Ein <slot>-Element projiziert den vorhandenen ("Light DOM"-)Inhalt des Hosts in den Shadow Tree neben dem eigenen Markup der Komponente.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Shadow DOM Example</title>
    <style>
        .card {
            padding: 20px;
            margin: 10px;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <div id="shadow-host" class="card">
        <span>This is the light DOM content</span>
    </div>

    <script>
        const host = document.getElementById('shadow-host');
        const shadowRoot = host.attachShadow({ mode: 'open' });

        const fragment = document.createDocumentFragment();
        const style = document.createElement('style');
        style.textContent = `.shadow-card { padding: 20px; margin: 10px; border: 1px solid blue; color: blue; }`;
        const slot = document.createElement('slot');
        const card = document.createElement('div');
        card.className = 'shadow-card';
        card.textContent = 'This is inside the Shadow DOM';

        fragment.appendChild(style);
        fragment.appendChild(slot);
        fragment.appendChild(card);
        shadowRoot.appendChild(fragment);
    </script>
</body>
</html>

Dieses Beispiel erstellt einen Shadow Tree auf #shadow-host und fügt Stile und Inhalt in ihn ein. Der Light DOM-Inhalt (This is the light DOM content) verbleibt im Host und wird durch das <slot>-Element im Shadow Tree sichtbar, sodass er neben dem Shadow-Inhalt erscheint, anstatt von ihm ersetzt zu werden.

Beachten Sie, was die Kapselung für Stile tut und was nicht. Die .shadow-card-Regel lebt innerhalb des Shadow Trees und gilt nur für Knoten in diesem Baum; sie kann .card an anderer Stelle auf der Seite nicht treffen, und eine .card-Regel auf der Seite kann nicht in den Shadow Tree eindringen. Die einzige Ausnahme bilden vererbbare Eigenschaftencolor, font-family, line-height und ähnliche — die weiterhin über die Grenze in geslotteten Inhalt fließen. Die Kapselung blockiert die Selektorübereinstimmung, nicht die Vererbung. Für eine tiefere Auseinandersetzung lesen Sie Shadow DOM stylen und Slots und Komposition.

Wann welche Technik zu wählen ist

  • Verwenden Sie <template>, wenn Sie dasselbe Markup wiederholt instanziieren (Listenzeilen, Karten, Modals) und es deklarativ in HTML definieren möchten.
  • Verwenden Sie Shadow DOM, wenn ein Widget Stile benötigt, die nicht mit der Host-Seite kollidieren dürfen — eine Design-System-Schaltfläche, ein Datums-Picker, ein einbettbares Widget.
  • Kombinieren Sie beides — definieren Sie Markup in einem <template> und klonen Sie es in eine Shadow Root — um vollständig wiederverwendbare Custom Elements zu erstellen.

Best Practices

  • Bevorzugen Sie DocumentFragment für Batch-Einfügungen: Das Anhängen eines Fragments an eine Shadow Root (oder einen beliebigen Container) in einer einzigen Operation minimiert Layout-Neuberechnungen und verbessert die Rendering-Leistung.
  • Klone vor dem Einfügen befüllen: Abfragen und füllen Sie das geklonte Fragment, solange es noch losgelöst ist, damit der Browser nur einen einzigen Reflow durchführt, wenn Sie es anhängen.
  • Wählen Sie den open-Shadow-Modus: Er macht Komponenten debuggbar und testbar; closed bietet keine echte Sicherheit.
  • Verwenden Sie document.importNode() über Dokumente hinweg: Beim Klonen von Inhalten aus einem anderen Dokument oder iframe stellt importNode die korrekte Knoteneigentümerschaft sicher und verhindert dokumentübergreifende Fehler.
  • Halten Sie den Light DOM minimal: Verwenden Sie <slot>-Elemente, um nur den Inhalt zu projizieren, der wirklich zur Seite gehört, und halten Sie den Host vorhersehbar.
Info

Nutzen Sie Shadow DOM, um Stile und Funktionalität innerhalb von Komponenten zu kapseln, Stilkonflikte zu vermeiden und modularen, wartbaren Code zu gewährleisten.

Fazit

Fortgeschrittene DOM-Techniken wie die Verwendung von Templates und Shadow DOM sind leistungsstarke Werkzeuge zum Erstellen modularer, wartbarer und effizienter Webanwendungen. Durch die Kapselung von Komponenten-Stilen und -Verhalten sowie die Nutzung wiederverwendbarer Templates können Sie Ihren Entwicklungsworkflow verbessern und robuste Webanwendungen erstellen.

Übungen

Übung
Welche der folgenden Aussagen über fortgeschrittene DOM-Techniken sind wahr?
Welche der folgenden Aussagen über fortgeschrittene DOM-Techniken sind wahr?
Was this page helpful?