CSS und Stile
In diesem Artikel werden weitere praktische Funktionen von Shadow DOM beschrieben. Es basiert auf den im Shadow DOM 101 behandelten Konzepten. Eine Einführung finden Sie in diesem Artikel.
Einleitung
Seien wir ehrlich. Unformatiertes Markup ist nicht sexy. Zum Glück haben die brillanten Leute hinter Web Components dies vorausahnt und uns nicht hängen gelassen. Im CSS-Scoping-Modul werden viele Optionen für die Gestaltung von Inhalten in einem Schattenbaum definiert.
Stilkapselung
Eines der Kernmerkmale von Shadow DOM ist die Schattengrenze. Es hat viele schöne Eigenschaften, aber eine der besten ist, dass es die Stilkapselung kostenlos zur Verfügung stellt. Anders ausgedrückt:
<div><h3>Light DOM</h3></div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.innerHTML = `
<style>
h3 {
color: red;
}
</style>
<h3>Shadow DOM</h3>
`;
</script>
Es gibt zwei interessante Beobachtungen bei dieser Demo:
- Auf dieser Seite gibt es noch andere h3-Werte, aber das einzige, das mit dem h3-Selektor übereinstimmt und daher rot formatiert ist, ist das h3-Element im ShadowRoot. Auch hier sind standardmäßig auf einen Bereich reduzierte Stile festgelegt.
- Andere auf dieser Seite definierte Stilregeln für h3s werden nicht in meinen Inhalt übernommen. Das liegt daran, dass Selektoren nicht die Schattengrenze überschreiten.
Die Moral der Geschichte? Wir bieten Stilkapselung von der Außenwelt. Danke, Shadow DOM!
Hostelement gestalten
Mit :host
können Sie das Element, das einen Schattenbaum hostet, auswählen und gestalten:
<button class="red">My Button</button>
<script>
var button = document.querySelector('button');
var root = button.createShadowRoot();
root.innerHTML = `
<style>
:host {
text-transform: uppercase;
}
</style>
<content></content>
`;
</script>
Ein Problem ist, dass Regeln auf der übergeordneten Seite eine höhere Spezifität als die im Element definierten :host
-Regeln haben, aber eine geringere Spezifität als ein style
-Attribut, das im Hostelement definiert ist. So können Nutzer Ihren Stil von außen überschreiben.
:host
funktioniert auch nur in Verbindung mit ShadowRoot, d. h., es kann nicht außerhalb von Shadow DOM verwendet werden.
Mit der funktionalen Form von :host(<selector>)
können Sie das Hostelement ausrichten, wenn es einem <selector>
entspricht.
Beispiel: Damit wird nur abgeglichen, wenn das Element selbst die Klasse .different
hat (z.B. <x-foo class="different"></x-foo>
):
:host(.different) {
...
}
Auf Nutzerstatus reagieren
:host
wird häufig verwendet, wenn Sie ein Custom Element erstellen und auf verschiedene Nutzerstatus reagieren möchten (:hover, :focus, :active usw.).
<style>
:host {
opacity: 0.4;
transition: opacity 420ms ease-in-out;
}
:host(:hover) {
opacity: 1;
}
:host(:active) {
position: relative;
top: 3px;
left: 3px;
}
</style>
Einem Element ein Design zuweisen
Die Pseudoklasse :host-context(<selector>)
stimmt mit dem Hostelement überein, wenn es oder einer seiner Ancestors mit <selector>
übereinstimmt.
:host-context()
wird häufig verwendet, um ein Element basierend auf seiner Umgebung mit einem Thema zu versehen. Viele Nutzer führen beispielsweise Themen aus, indem sie eine Klasse auf <html>
oder <body>
anwenden:
<body class="different">
<x-foo></x-foo>
</body>
Sie können :host-context(.different)
verwenden, um <x-foo>
zu gestalten, wenn es ein Nachfolgerelement eines Elements mit der Klasse .different
ist:
:host-context(.different) {
color: red;
}
So haben Sie die Möglichkeit, Stilregeln im Shadow-DOM eines Elements zu kapseln, die es je nach Kontext individuell gestalten.
Unterstützung mehrerer Hosttypen von einem Schattenstamm aus
:host
wird auch verwendet, wenn Sie eine Designbibliothek erstellen und das Gestalten vieler Arten von Hostelementen innerhalb desselben Shadow-DOMs unterstützen möchten.
:host(x-foo) {
/* Applies if the host is a <x-foo> element.*/
}
:host(x-foo:host) {
/* Same as above. Applies if the host is a <x-foo> element. */
}
:host(div) {
/* Applies if the host element is a <div>. */
}
Interne Elemente des Shadow-DOM von außen gestalten
Das Pseudoelement ::shadow
und das Kombinationselement /deep/
sind wie ein Vorpal-Schwert für CSS-Autorität.
Sie ermöglichen das Durchdringen der Grenzen des Shadow DOM, um Elemente innerhalb von Schattenbäumen zu gestalten.
Das ::shadow-Pseudoelement
Wenn ein Element mindestens einen Schattenbaum hat, stimmt das Pseudoelement ::shadow
mit dem Schattenstamm selbst überein.
Damit können Sie Selektoren schreiben, die Knoten innerhalb des Schattenbereichs eines Elements gestalten.
Wenn ein Element beispielsweise einen Schattenstamm hostet, können Sie #host::shadow span {}
schreiben, um alle Spans innerhalb des Schattenbaums zu gestalten.
<style>
#host::shadow span {
color: red;
}
</style>
<div id="host">
<span>Light DOM</span>
</div>
<script>
var host = document.querySelector('div');
var root = host.createShadowRoot();
root.innerHTML = `
<span>Shadow DOM</span>
<content></content>
`;
</script>
Beispiel (benutzerdefinierte Elemente): Das Shadow-DOM von <x-tabs>
hat <x-panel>
untergeordnete Elemente. Jedes Feld hostet seinen eigenen Schattenbaum mit h2
-Überschriften. Um diese Überschriften auf der Hauptseite zu gestalten, könnten Sie Folgendes schreiben:
x-tabs::shadow x-panel::shadow h2 {
...
}
Der /deep/-Combinator
Der Kombinator /deep/
ähnelt ::shadow
, ist aber leistungsfähiger. Alle Schattengrenzen werden vollständig ignoriert und er geht in beliebig viele Schattenbäume über. Einfach ausgedrückt können Sie mit /deep/
die Eingeweide eines Elements untersuchen und ein Targeting auf jeden beliebigen Knoten vornehmen.
Die Kombination /deep/
ist besonders nützlich bei benutzerdefinierten Elementen, in denen häufig mehrere Ebenen von Shadow DOM verwendet werden. Erste Beispiele sind das Verschachteln einer Reihe von benutzerdefinierten Elementen, wobei jedes Element einen eigenen Schattenbaum hostet, oder mithilfe von <shadow>
ein Element, das von einem anderen übernommen wird.
Beispiel (benutzerdefinierte Elemente): Wählen Sie an einer beliebigen Stelle in der Struktur alle <x-panel>
-Elemente aus, die Nachfolger von <x-tabs>
sind:
x-tabs /deep/ x-panel {
...
}
Beispiel: Gestalten Sie alle Elemente mit der Klasse .library-theme
an einer beliebigen Stelle in einem Schattenbaum:
body /deep/ .library-theme {
...
}
Mit querySelector() arbeiten
Ähnlich wie .shadowRoot
Schattenbäume für den DOM-Durchlauf öffnet, öffnen die Combinators Schattenbäume für den Selektordurchlauf.
Anstatt eine verschachtelte Kette des Wahnsinns zu schreiben, können Sie eine einzelne Anweisung schreiben:
// No fun.
document.querySelector('x-tabs').shadowRoot
.querySelector('x-panel').shadowRoot
.querySelector('#foo');
// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');
Stile für native Elemente festlegen
Die Gestaltung nativer HTML-Steuerelemente ist eine Herausforderung. Viele Leute geben einfach auf
und rollen das. Mit ::shadow
und /deep/
kann jedoch jedes Element auf der Webplattform, das Shadow DOM verwendet, gestaltet werden. Gute Beispiele sind die <input>
-Typen und <video>
:
video /deep/ input[type="range"] {
background: hotpink;
}
Stil-Hooks erstellen
Anpassung ist gut. In bestimmten Fällen möchten Sie vielleicht Löcher in das Stilschild des Schattens stechen und Haken erstellen, die andere gestalten können.
Mit ::shadow und /deep/
/deep/
hat eine Menge Energie. Damit können Komponentenautoren einzelne Elemente als Stilbar oder eine Reihe von Elementen als thematisch geeignet kennzeichnen.
Beispiel: Gestalten Sie alle Elemente der Klasse .library-theme
und ignorieren Sie alle Schattenbäume:
body /deep/ .library-theme {
...
}
Benutzerdefinierte Pseudoelemente verwenden
Sowohl WebKit als auch Firefox definieren Pseudoelemente zum Gestalten interner Elemente nativer Browserelemente. Ein gutes Beispiel ist input[type=range]
. Sie können den Schieberegler <span style="color:blue">blue</span>
gestalten, indem Sie ::-webkit-slider-thumb
ausrichten:
input[type=range].custom::-webkit-slider-thumb {
-webkit-appearance: none;
background-color: blue;
width: 10px;
height: 40px;
}
Ähnlich wie Browser, die Stil-Hooks in einige Internaten bereitstellen, können Autoren von Shadow DOM-Inhalten bestimmte Elemente als für Fremde nutzbar festlegen. Dies erfolgt mithilfe von benutzerdefinierten Pseudoelementen.
Mit dem Attribut pseudo
können Sie ein Element als benutzerdefiniertes Pseudoelement festlegen.
Sein Wert oder Name muss das Präfix „x-“ haben. Dadurch wird eine Verknüpfung mit diesem Element im Schattenbaum hergestellt und Außenstehenden wird eine bestimmte Spur geboten, über die sie die Schattengrenze überqueren können.
Hier sehen Sie ein Beispiel für das Erstellen eines benutzerdefinierten Schieberegler-Widgets, mit dem jemand seinen Schieberegler-Daumen blau gestalten kann:
<style>
#host::x-slider-thumb {
background-color: blue;
}
</style>
<div id="host"></div>
<script>
var root = document.querySelector('#host').createShadowRoot();
root.innerHTML = `
<div>
<div pseudo="x-slider-thumb"></div>' +
</div>
`;
</script>
CSS-Variablen verwenden
Eine wirkungsvolle Methode zum Erstellen von Design-Hooks sind CSS-Variablen. Im Wesentlichen erstellen Sie „Stilplatzhalter“, die von anderen Nutzern ausgefüllt werden können.
Stellen Sie sich einen Autor eines benutzerdefinierten Elements vor, der Variablenplatzhalter in seinem Shadow DOM markiert. Eines für den Stil der Schriftart einer internen Schaltfläche und eine weitere für die Farbe:
button {
color: var(--button-text-color, pink); /* default color will be pink */
font-family: var(--button-font);
}
Dann definiert der Einbettunger des Elements diese Werte nach Bedarf. Vielleicht passend zum coolen Comic Sans-Thema der eigenen Seite:
#host {
--button-text-color: green;
--button-font: "Comic Sans MS", "Comic Sans", cursive;
}
Aufgrund der Art und Weise, wie CSS-Variablen übernommen werden, ist alles pfirsichfarben und das funktioniert wunderbar. Das ganze Bild sieht so aus:
<style>
#host {
--button-text-color: green;
--button-font: "Comic Sans MS", "Comic Sans", cursive;
}
</style>
<div id="host">Host node</div>
<script>
var root = document.querySelector('#host').createShadowRoot();
root.innerHTML = `
<style>
button {
color: var(--button-text-color, pink);
font-family: var(--button-font);
}
</style>
<content></content>
`;
</script>
Stile zurücksetzen
Übernommene Stile wie Schriftarten, Farben und Linienhöhen wirken sich weiterhin auf Elemente im Shadow-DOM aus. Für maximale Flexibilität bietet uns das Schatten-DOM die Eigenschaft resetStyleInheritance
, mit der wir steuern können, was an der Schattengrenze geschieht.
Stellen Sie sich dies als eine Möglichkeit vor, bei der Erstellung einer neuen Komponente von vorn zu beginnen.
resetStyleInheritance
false
ist die Standardeinstellung. Übernommene CSS-Eigenschaften werden weiterhin übernommen.true
: Setzt vererbbare Attribute an der Grenze aufinitial
zurück.
In der folgenden Demo sehen Sie, wie sich eine Änderung von resetStyleInheritance
auf den Schattenbaum auswirkt:
<div>
<h3>Light DOM</h3>
</div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.resetStyleInheritance = <span id="code-resetStyleInheritance">false</span>;
root.innerHTML = `
<style>
h3 {
color: red;
}
</style>
<h3>Shadow DOM</h3>
<content select="h3"></content>
`;
</script>
<div class="demoarea" style="width:225px;">
<div id="style-ex-inheritance"><h3 class="border">Light DOM</div>
</div>
<div id="inherit-buttons">
<button id="demo-resetStyleInheritance">resetStyleInheritance=false</button>
</div>
<script>
var container = document.querySelector('#style-ex-inheritance');
var root = container.createShadowRoot();
//root.resetStyleInheritance = false;
root.innerHTML = '<style>h3{ color: red; }</style><h3>Shadow DOM<content select="h3"></content>';
document.querySelector('#demo-resetStyleInheritance').addEventListener('click', function(e) {
root.resetStyleInheritance = !root.resetStyleInheritance;
e.target.textContent = 'resetStyleInheritance=' + root.resetStyleInheritance;
document.querySelector('#code-resetStyleInheritance').textContent = root.resetStyleInheritance;
});
</script>
.resetStyleInheritance
ist etwas schwieriger zu verstehen, da es sich nur auf CSS-Attribute auswirkt, die vererbbar sind. Diese besagt: Wenn Sie nach einer Property suchen, die übernommen werden soll, übernehmen Sie an der Grenze zwischen der Seite und dem ShadowRoot keine Werte vom Host, sondern verwenden Sie stattdessen den Wert initial
(gemäß der CSS-Spezifikation).
Wenn Sie nicht sicher sind, welche Eigenschaften in CSS übernommen werden, sehen Sie sich diese praktische Liste an oder aktivieren Sie im Steuerfeld „Element“ das Kästchen „Übernommene Elemente anzeigen“.
Verteilte Knoten gestalten
Verteilte Knoten sind Elemente, die an einem Einfügepunkt (einem <content>
-Element) gerendert werden. Mit dem <content>
-Element können Sie Knoten aus dem Light DOM auswählen und an vordefinierten Positionen in Ihrem Shadow DOM rendern. Sie befinden sich nicht logisch im Shadow-DOM; sie sind immer noch untergeordnete Elemente des Hostelements. Einfügungen sind reines Rendering.
Verteilte Knoten behalten Stile aus dem Hauptdokument bei. Das heißt, die Stilregeln von der Hauptseite gelten weiterhin für die Elemente, auch wenn sie an einer Einfügestelle gerendert werden. Verteilte Knoten befinden sich immer noch logisch in der leichten domäne und bewegen sich nicht. Sie rendern einfach an anderer Stelle. Wenn die Knoten jedoch im Shadow DOM verteilt werden, können sie zusätzliche Stile annehmen, die innerhalb des Schattenbaums definiert sind.
::content-Pseudoelement
Verteilte Knoten sind untergeordnete Objekte des Hostelements. Wie können wir sie innerhalb des Shadow-DOMs anvisieren? Die Antwort ist das CSS-Pseudoelement ::content
.
Damit lässt sich ein Targeting auf Light-DOM-Knoten vornehmen, die einen Einfügepunkt passieren. Beispiel:
::content > h3
gestaltet alle h3
-Tags, die einen Einfügepunkt durchlaufen.
Hier ein Beispiel:
<div>
<h3>Light DOM</h3>
<section>
<div>I'm not underlined</div>
<p>I'm underlined in Shadow DOM!</p>
</section>
</div>
<script>
var div = document.querySelector('div');
var root = div.createShadowRoot();
root.innerHTML = `
<style>
h3 { color: red; }
content[select="h3"]::content > h3 {
color: green;
}
::content section p {
text-decoration: underline;
}
</style>
<h3>Shadow DOM</h3>
<content select="h3"></content>
<content select="section"></content>
`;
</script>
Stile an Einfügungspunkten zurücksetzen
Beim Erstellen eines ShadowRoot-Objekts kannst du die übernommenen Stile zurücksetzen.
Für <content>
und <shadow>
Platzhalter gibt es diese Option ebenfalls. Wenn du diese Elemente verwendest, musst du entweder .resetStyleInheritance
in JS festlegen oder das boolesche Attribut reset-style-inheritance
für das Element selbst verwenden.
Bei einem ShadowRoot- oder
<shadow>
-Einfügungspunkt bedeutetreset-style-inheritance
, dass vererbbare CSS-Eigenschaften auf dem Host aufinitial
gesetzt werden, bevor sie auf Ihren Schatteninhalt gelangen. Dieser Standort wird als Obergrenze bezeichnet.Bei
<content>
-Einfügungspunkten bedeutetreset-style-inheritance
, dass vererbbare CSS-Eigenschaften aufinitial
gesetzt werden, bevor die untergeordneten Elemente des Hosts am Einfügepunkt verteilt werden. Dieser Ort wird als Untergrenze bezeichnet.
Fazit
Als Autoren von benutzerdefinierten Elementen haben wir eine Vielzahl von Optionen, mit denen wir das Erscheinungsbild unserer Inhalte steuern können. Das Schatten-DOM bildet die Grundlage für diese neue Welt.
Das Shadow-DOM bietet eine bereichsspezifische Stilkapselung und die Möglichkeit, so viel (oder weniger) der Außenwelt zu berücksichtigen, wie wir möchten. Durch das Definieren benutzerdefinierter Pseudoelemente oder das Einbeziehen von Platzhaltern für CSS-Variablen können Autoren Dritten praktische Stil-Hooks zur Verfügung stellen, mit denen sie ihren Inhalt weiter anpassen können. Alles in allem haben Webautoren die volle Kontrolle darüber, wie ihre Inhalte dargestellt werden.