Web Components

Custom Elements, Templates und Shadow Dom

Informatik – 3. Semester

Software- und Web-Engineering I · TH Lübeck
1 Einführung
2 Templates & Shadow DOM
3 Custom Elements
4 Slots
5 Lifecycle-Callbacks
6 Übung: CustomProfileCard

Wiederverwendbare, gekapselte HTML-Bausteine

Software- und Web-Engineering I · TH Lübeck
Das Ziel
  • Code wiederverwenden zu können spart Zeit
  • Als Web-Developer:innen, haben wir uns vorgenommen, eine kleine Bibliothek mit fertig-gestylten HTML-Code-Schnipseln bauen, die wir häufig in Projekten wiederverwenden können.
  • Als Übung wollen wir testweise einen Container erschaffen, der sämtlichen inneren Text blau einfärbt, unabhängig vom globalen Styling der Seite.
Software- und Web-Engineering I · TH Lübeck
1 Einführung
2 Templates & Shadow DOM
3 Custom Elements
4 Slots
5 Lifecycle-Callbacks
6 Übung: CustomProfileCard

HTML-Schnipsel definieren und vor globalem CSS schützen

Software- und Web-Engineering I · TH Lübeck
Templates
  • Mit dem <template>-Element können Sie HTML-Schnipsel definieren, die vom Browser nicht gerendert werden
  • Per JavaScript können Sie Templates auswählen und anschließend den Inhalt duplizieren und wiederverwenden
<template id="blue-div">
  <div>
    <style>
      div {color:blue;}
    </style>
    This is inner text;
    <p>This is a paragraph.</p>
  </div>
</template>
Software- und Web-Engineering I · TH Lübeck
Templates
<div id="template-insert"></div>
<script>
  const blueDiv = document.querySelector(
    "#blue-div").content;
  const insert = document.querySelector(
    "#template-insert");
  for (var n = 0; n < 2; n++) {
    var clone = blueDiv.cloneNode(true);
    body.appendChild(clone);
  }
</script>
This is inner text;

This is a paragraph.

This is inner text;

This is a paragraph.

Software- und Web-Engineering I · TH Lübeck
Das Problem
  • Existierende CSS-Selektoren können mit den eingefügten Elementen überlappen
  • Hier färbt der <p>-Selektor unsere eingefügten <p>-Element rot ein, obwohl wir sie im <template>-Element blau gefärbt haben
<style> p {color: red;} </style>
<div id="template-insert"></div>
This is inner text;

This is a paragraph.

This is inner text;

This is a paragraph.

Software- und Web-Engineering I · TH Lübeck
Die Lösung: Shadow-DOM
  • Ein Shadow-DOM ist ein verkapselter DOM-Baum, der vor externem JavaScript und CSS versteckt ist
    • Ein Shadow-DOM wird der Haupt-DOM als verkapseltes Kind untergeordnet.
  • Das ermöglicht das Wiederverwenden von HTML-Code Schnipseln, unabhängig von externem Styling
Software- und Web-Engineering I · TH Lübeck
Shadow DOM
Document Tree, Shadow Tree und Flattened Tree: der Shadow Tree hängt über den Shadow Host am Dokument und wird zum Rendern eingeflacht
Software- und Web-Engineering I · TH Lübeck
Templates
<div id="template-insert"></div>
<script>
  const blueDiv = document.querySelector(
    "#blue-div").content;
  const insert = document.querySelector(
    "#template-insert");
  // Hier passiert die Magie
  const shadow = insert.attachShadow(
    { mode: "open" })
  for (var n = 0; n < 2; n++) {
    var clone = blueDiv.cloneNode(true);
    shadow.appendChild(clone);
  }
</script>
This is inner text;

This is a paragraph.

This is inner text;

This is a paragraph.

Software- und Web-Engineering I · TH Lübeck
1 Einführung
2 Templates & Shadow DOM
3 Custom Elements
4 Slots
5 Lifecycle-Callbacks
6 Übung: CustomProfileCard

Eigene HTML-Elemente als wiederverwendbare Klassen

Software- und Web-Engineering I · TH Lübeck
Custom Elements
class BlueDiv extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    const div = document.createElement('div');
    div.innerHTML = `
        <style> div {color: blue} </style>
        This is inner text;
        <p>This is a paragraph.</p>
    `;
    shadow.append(div);
  }
}
customElements.define('blue-div', BlueDiv);
Software- und Web-Engineering I · TH Lübeck
Custom Elements
<style> p {color: red;} </style>
<blue-div> </blue-div>
This is inner text;

This is a paragraph.

  • Unser eigenes HTML-Element in einer Seite
  • Das <p>-Element von unserem <blue-div>-Element wird vom globalen Selektor dank der Shadow Root ignoriert
Software- und Web-Engineering I · TH Lübeck
1 Einführung
2 Templates & Shadow DOM
3 Custom Elements
4 Slots
5 Lifecycle-Callbacks
6 Übung: CustomProfileCard

Inhalte von außen in eine Komponente einspeisen

Software- und Web-Engineering I · TH Lübeck
Slots
  • Mit dem <slot>-Element können Sie HTML-Code in existierende Templates oder Custom Elements injizieren
Software- und Web-Engineering I · TH Lübeck
Slots: Default-Inhalt
<template id="info-card-tpl">
  <div>
    <slot>Kein Inhalt angegeben</slot>
  </div>
</template>
<script>
  customElements.define('info-card',
    class extends HTMLElement {
      constructor() {
        super();
        const root = this.attachShadow(
          { mode: 'open' });
        const tpl = document.querySelector(
          '#info-card-tpl');
        root.appendChild(
          tpl.content.cloneNode(true));
      }
    });
</script>
<info-card></info-card>
<info-card>Hallo Welt!</info-card>
Kein Inhalt angegeben
Hallo Welt!
Software- und Web-Engineering I · TH Lübeck
Slots: Benannte Slots
<template id="info-card-tpl">
  <div>
    <h3><slot name="title">
      Titel</slot></h3>
    <slot>Kein Inhalt</slot>
  </div>
</template>
  • Ein slot="..."-Attribut im Light-DOM verweist auf den passenden name im <slot>-Element
  • So lassen sich mehrere Einfüge-Stellen in einer Komponente unterscheiden
<info-card>
  <span slot="title">
    Wichtiger Hinweis</span>
  Der Server ist um 22 Uhr
  offline.
</info-card>
Wichtiger Hinweis
Der Server ist um 22 Uhr offline.
Software- und Web-Engineering I · TH Lübeck
1 Einführung
2 Templates & Shadow DOM
3 Custom Elements
4 Slots
5 Lifecycle-Callbacks
6 Übung: CustomProfileCard

Wann wird ein Custom Element aktiv?

Software- und Web-Engineering I · TH Lübeck
Lifecycle-Callbacks
Callback Wann? Zweck
constructor() Beim Erzeugen des Objekts, noch nicht im DOM Shadow-Root anlegen, Grundzustand
connectedCallback() Element wird in den DOM eingefügt Attribute auslesen, Inhalt aufbauen
disconnectedCallback() Element wird aus dem DOM entfernt Aufräumen (Listener, Timer)
attributeChangedCallback() Ein beobachtetes Attribut ändert sich Auf die Änderung reagieren
static get observedAttributes() Einmalig beim Registrieren der Klasse Legt fest, welche Attribute beobachtet werden
Attribute erst im connectedCallback auslesen, nicht im Constructor: die Spezifikation garantiert dort noch keine vollständig geparsten Attribute.
Software- und Web-Engineering I · TH Lübeck
Lifecycle-Callbacks: Beispiel
class ColorBox extends HTMLElement {
  static get observedAttributes() {
    return ['color'];
  }

  connectedCallback() {
    this.style.display = 'block';
    this.style.backgroundColor =
      this.getAttribute('color');
  }

  attributeChangedCallback(name, oldVal, newVal) {
    if (name === 'color') {
      this.style.backgroundColor = newVal;
    }
  }

  disconnectedCallback() {
    console.log('color-box entfernt');
  }
}
customElements.define('color-box', ColorBox);
Software- und Web-Engineering I · TH Lübeck
1 Einführung
2 Templates & Shadow DOM
3 Custom Elements
4 Slots
5 Lifecycle-Callbacks
6 Übung: CustomProfileCard

Die Profilkarte selbst bauen

Software- und Web-Engineering I · TH Lübeck
CustomProfileCard
  • Implementieren Sie das HTML-Element "Profile Card", dass ein Bild, einen Namen und eine Rolle als Attribut annimmt und ein HTML-Gerüst für eine Profilkarte zurückgibt
  • Die Profilkarte soll Slot Element für einen Beschreibungstext enthalten
  • Binden Sie das mitgelieferte Stylesheet in die Profilkarte ein
  • Welche Elemente die Karte enhält entnehmen Sie bitte diesem Stylesheet
  • Lernraumkurs → Zusatzmaterial → Übung: CustomProfileCard
20 Min
QR-Code zur Übung CustomProfileCard
Software- und Web-Engineering I · TH Lübeck
Lösung: CustomProfileCard – Vorgehen
  • Schritt 1: Stylesheet als <link>-Element erzeugen und in den Shadow-DOM einhängen
  • Schritt 2: Card-Grundgerüst (div.card) bauen
  • Schritt 3: Attribute (name, img, role) auslesen und in die passenden Elemente schreiben
  • Schritt 4: <slot name="description"> für den mitgelieferten Beschreibungstext ergänzen
  • Alle vier Schritte passieren im connectedCallback, nicht im Constructor
Software- und Web-Engineering I · TH Lübeck
Lösung: Stylesheet & Grundgerüst
connectedCallback() {
  const shadow = this.shadowRoot;

  const link = document.createElement('link');
  link.setAttribute('rel', 'stylesheet');
  link.setAttribute('href', 'profile-card.css');
  shadow.appendChild(link);

  const card = document.createElement('div');
  card.classList.add('card');

  const img = document.createElement('img');
  img.setAttribute('src', this.getAttribute('img'));
  card.appendChild(img);
Software- und Web-Engineering I · TH Lübeck
Lösung: Attribute & Slot (Forts.)
  const name = document.createElement('h2');
  name.textContent = this.getAttribute('name');
  card.appendChild(name);

  const role = document.createElement('small');
  role.textContent = this.getAttribute('role');
  card.appendChild(role);

  const slot = document.createElement('slot');
  slot.setAttribute('name', 'description');
  card.appendChild(slot);

  shadow.appendChild(card);
}
Software- und Web-Engineering I · TH Lübeck
Lösung: Ergebnis
<profile-card name="Ada Lovelace"
  role="Mathematician"
  img="ada.jpg">
  <p slot="description">
    Erste Programmiererin.
  </p>
</profile-card>
Ada Lovelace
Mathematician
Erste Programmiererin.
Software- und Web-Engineering I · TH Lübeck

- Einstieg: heute bauen wir eigene, wiederverwendbare HTML-Elemente - Drei Bausteine ankuendigen: Templates, Shadow DOM, Custom Elements - Nicht auf dem Titel verweilen

- Fahrplan fuer die Vorlesung zeigen: sechs Stationen bis zur eigenen Profilkarte - Kurz halten, direkt weiter zum Ziel der Stunde

- Motivation: wiederverwendbare, fertig gestylte Bausteine wie in einer Komponenten-Bibliothek - Laufendes Beispiel der naechsten Folien: ein Container, der Text blau faerbt

- Ueberleitung von der Motivation zum ersten Baustein: das template-Element - Danach das Problem (globales CSS) und die Loesung (Shadow DOM) zeigen

- Kernpunkt: Inhalt eines template-Elements wird vom Browser nicht gerendert - Erst per JavaScript ausgewaehlt, geklont und eingefuegt

- Ablauf am Code zeigen: content des Templates holen, mit cloneNode(true) duplizieren, anhaengen - Ergebnis rechts: der Schnipsel erscheint zweimal, komplett blau

- Problem herausarbeiten: globales CSS der Seite greift auch auf die eingefuegten Elemente - Der spaeter geladene p-Selektor gewinnt, die Absaetze werden rot statt blau

- Kernbegriff: Verkapselung, der Shadow-Baum ist von aussen nicht stylebar - Genau das loest unser Problem von der vorherigen Folie

- Begriffe am Bild erklaeren: Shadow Host (Element im Haupt-DOM), Shadow Root, Shadow Boundary - Rechts: zum Rendern werden beide Baeume zu einem Flattened Tree zusammengefuehrt

- Eine neue Zeile macht den Unterschied: attachShadow({ mode: "open" }) am Einfuege-Element - Klone landen im Shadow Root statt im Haupt-DOM, alles bleibt blau

- Templates und Shadow DOM sind die Bausteine, Custom Elements verpacken sie in einen eigenen Tag - Ueberleitung: statt einzufuegen schreiben wir gleich unser eigenes HTML-Element

- Custom Element = eigene Klasse, die von HTMLElement erbt - Im Konstruktor: Shadow Root anhaengen und Inhalt hineinbauen - customElements.define registriert den Tag-Namen (muss einen Bindestrich enthalten)

- Einsatz wie ein normales HTML-Element: einfach <blue-div> schreiben - Gegenprobe zum Problem von vorhin: der rote p-Selektor greift nicht mehr

- Bisher war der Inhalt eines Custom Elements fest im Konstruktor verdrahtet - Slots loesen das: Nutzer:innen der Komponente liefern eigenen Inhalt von aussen

- Slots als Uebergabe-Stellen: Nutzer des Elements liefern eigenen Inhalt hinein - Ueberleitung zur Uebung: genau das braucht die Profilkarte fuer den Beschreibungstext

- Ein slot ohne name-Attribut ist der Default-Slot: er nimmt jeden nicht anders zugewiesenen Inhalt auf - Bleibt das Custom Element leer, greift der Fallback-Text, der im slot-Element selbst steht

- Benannte Slots erlauben mehrere Einfuegestellen in einer Komponente statt nur einer - Ueberleitung: CustomProfileCard braucht genau das fuer den Beschreibungstext

- Letzter Baustein vor der Uebung: die Lifecycle-Callbacks eines Custom Elements - Wichtig fuer die Uebung gleich: wann darf ich welche Attribute lesen?

- Tabelle als Nachschlagewerk: vier Callbacks plus die observedAttributes-Deklaration - Kernregel hervorheben: Attribute nie im Constructor lesen, das gilt gleich auch fuer die Uebung

- observedAttributes legt fest, dass nur Aenderungen am color-Attribut attributeChangedCallback ausloesen - connectedCallback setzt die Startfarbe hier, nicht im Constructor, weil das Element da schon im DOM haengt

- Letzte Station: alle Bausteine (Custom Element, Shadow DOM, Slot, Lifecycle) in einer Uebung kombinieren - Direkt in die Aufgabenstellung ueberleiten

- Uebung austeilen: Material liegt im Lernraum unter Zusatzmaterial, QR-Code zeigen - Dry-Run-Stolperstelle: QR-Code (assets/web-06/qr-custom-profile-card.png) zeigt auf den 25/26-Lernraumkurs — vor dem Halten fuer den neuen Kurs neu generieren - Hinweis: Attribute (img, name, role) auslesen, Slot fuer die Beschreibung, Stylesheet einbinden - 20 Minuten Arbeitszeit, danach kurze Besprechung

- Vier Schritte als Fahrplan fuer die Live-Codierung der Loesung - Kernregel wiederholen: alles im connectedCallback, Attribute existieren im Constructor noch nicht zuverlaessig

- link.setAttribute kombiniert die Hinweise 1-3 der Aufgabe: Element erzeugen, Attribute setzen, einhaengen - classList.add('card') muss zur CSS-Klasse aus profile-card.css passen

- h2 und small matchen die Selektoren aus profile-card.css - slot mit name="description" muss zum slot="description" im HTML der Aufgabe passen - Zum Schluss: card an den shadow anhaengen, nicht direkt an this

- Ergebnis zeigen: eine fertige Profilkarte mit Foto, Name, Rolle und Slot-Text - Kurzer Rueckblick: Custom Element, Shadow DOM, externes Stylesheet und Slot, alles aus dieser Vorlesung kombiniert