Hier erfahren Sie, wie Sie mit @scope Elemente nur innerhalb eines begrenzten Teilbaums Ihres DOM auswählen.
Die schwierige Kunst, CSS-Selektoren zu schreiben
Beim Erstellen von Auswahlelementen stehen Sie möglicherweise zwischen zwei Welten. Einerseits sollten Sie ziemlich genau festlegen, welche Elemente Sie auswählen. Andererseits sollten Ihre Selectors einfach überschrieben werden können und nicht eng mit der DOM-Struktur verbunden sein.
Wenn Sie beispielsweise „das Hero-Image im Inhaltsbereich der Kartenkomponente“ auswählen möchten, was eine ziemlich spezifische Elementauswahl ist, sollten Sie höchstwahrscheinlich keine Auswahl wie .card > .content > img.hero
schreiben.
- Dieser Selektor hat eine ziemlich hohe Spezifizität von
(0,3,1)
, was es schwierig macht, ihn zu überschreiben, wenn der Code wächst. - Da er auf dem Direktuntergeordneten-Kombinator basiert, ist er eng mit der DOM-Struktur verknüpft. Sollte sich das Markup ändern, müssen Sie auch das CSS ändern.
Sie sollten aber auch nicht nur img
als Selektor für dieses Element eingeben, da dadurch alle Bildelemente auf der Seite ausgewählt würden.
Das richtige Gleichgewicht zu finden, ist oft eine echte Herausforderung. Im Laufe der Jahre haben einige Entwickler Lösungen und Umgehungsmöglichkeiten entwickelt, die Ihnen in solchen Situationen helfen können. Beispiel:
- Bei Methoden wie BEM wird empfohlen, diesem Element die Klasse
card__img card__img--hero
zuzuweisen, um die Spezifität niedrig zu halten und gleichzeitig präzise auszuwählen. - JavaScript-basierte Lösungen wie begrenztes CSS oder Styled Components überschreiben alle Ihre Selektoren, indem sie Ihren Selektoren zufällig generierte Strings wie
sc-596d7e0e-4
hinzufügen, um zu verhindern, dass sie auf Elemente auf der anderen Seite Ihrer Seite ausgerichtet werden. - Einige Bibliotheken haben Selectoren sogar ganz abgeschafft und erfordern, dass Sie die Stilauslöser direkt in das Markup einfügen.
Aber was ist, wenn Sie keine davon benötigen? Was wäre, wenn Sie mit CSS ziemlich genau festlegen könnten, welche Elemente Sie auswählen, ohne dass Sie sehr spezifische Selektoren schreiben oder solche verwenden müssten, die eng mit Ihrem DOM verbunden sind? Hier kommt @scope
ins Spiel. Damit können Sie Elemente nur innerhalb eines untergeordneten Knotens Ihres DOM auswählen.
Jetzt neu: @scope
Mit @scope
können Sie die Reichweite Ihrer Auswahlkriterien einschränken. Dazu legen Sie den Begrenzungsknoten fest, der die Obergrenze des untergeordneten Knotens bestimmt, auf den Sie Ihre Anzeigen ausrichten möchten. Wenn ein Wurzelknoten festgelegt ist, können die enthaltenen Stilregeln, die sogenannten Stilregeln mit Bereich, nur aus diesem begrenzten Teilbaum des DOM ausgewählt werden.
Wenn Sie beispielsweise nur die <img>
-Elemente in der .card
-Komponente ansteuern möchten, legen Sie .card
als Wurzelelement der At-Rule @scope
fest.
@scope (.card) {
img {
border-color: green;
}
}
Mit der Stilregel mit Bereich img { … }
können nur <img>
-Elemente ausgewählt werden, die sich im Bereich des übereinstimmenden .card
-Elements befinden.
Wenn Sie verhindern möchten, dass die <img>
-Elemente im Inhaltsbereich der Karte (.card__content
) ausgewählt werden, können Sie den img
-Selektor präzisieren. Eine andere Möglichkeit besteht darin, dass die At-rule @scope
auch ein Begrenzungslimit akzeptiert, das die Untergrenze bestimmt.
@scope (.card) to (.card__content) {
img {
border-color: green;
}
}
Diese stilspezifische Regel gilt nur für <img>
-Elemente, die im übergeordneten Stammbaum zwischen .card
- und .card__content
-Elementen platziert sind. Diese Art der Begrenzung mit Ober- und Untergrenze wird oft als Donut-Begrenzung bezeichnet.
:scope
-Auswahl
Standardmäßig beziehen sich alle stilistischen Regeln mit Bereich auf den Stamm des Bereichs. Es ist auch möglich, das Stammelement des Gültigkeitsbereichs selbst als Ziel auszuwählen. Verwenden Sie dazu die Auswahl :scope
.
@scope (.card) {
:scope {
/* Selects the matched .card itself */
}
img {
/* Selects img elements that are a child of .card */
}
}
Selektoren in stilspezifischen Regeln werden implizit mit :scope
vorangestellt. Wenn Sie möchten, können Sie dies auch explizit angeben, indem Sie :scope
voranstellen. Alternativ können Sie den Selektor &
über das CSS-Verschachteln voranstellen.
@scope (.card) {
img {
/* Selects img elements that are a child of .card */
}
:scope img {
/* Also selects img elements that are a child of .card */
}
& img {
/* Also selects img elements that are a child of .card */
}
}
Für eine Begrenzung des Gültigkeitsbereichs kann die Pseudoklasse :scope
verwendet werden, um eine bestimmte Beziehung zum Gültigkeitsbereichsknoten zu erfordern:
/* .content is only a limit when it is a direct child of the :scope */
@scope (.media-object) to (:scope > .content) { ... }
Mit :scope
kann ein Begrenzungslimit auch auf Elemente außerhalb des Begrenzungsstamms verweisen. Beispiel:
/* .content is only a limit when the :scope is inside .sidebar */
@scope (.media-object) to (.sidebar :scope .content) { ... }
Die Stilregeln mit Bereich können den untergeordneten Knoten nicht selbst ausschließen. Auswahlen wie :scope + p
sind ungültig, da damit versucht wird, Elemente auszuwählen, die nicht zum Geltungsbereich gehören.
@scope
und Spezifität
Die Auswahlkriterien, die Sie im Vorspann für @scope
verwenden, haben keine Auswirkungen auf die Spezifität der enthaltenen Auswahlkriterien. Im folgenden Beispiel ist die Spezifität des img
-Selektors weiterhin (0,0,1)
.
@scope (#sidebar) {
img { /* Specificity = (0,0,1) */
…
}
}
:scope
ist eine reguläre Pseudoklasse, nämlich (0,1,0)
.
@scope (#sidebar) {
:scope img { /* Specificity = (0,1,0) + (0,0,1) = (0,1,1) */
…
}
}
Im folgenden Beispiel wird der &
intern in den Selektor umgeschrieben, der für den Wurzelknoten des Gültigkeitsbereichs verwendet wird, und in einen :is()
-Selektor eingehüllt. Der Browser verwendet dann :is(#sidebar, .card) img
als Auswahl für die Übereinstimmung. Dieser Vorgang wird als Entzuckerung bezeichnet.
@scope (#sidebar, .card) {
& img { /* desugars to `:is(#sidebar, .card) img` */
…
}
}
Da &
mit :is()
desugared wird, wird die Spezifität von &
gemäß den Spezifitätsregeln für :is()
berechnet: Die Spezifität von &
entspricht der des spezifischsten Arguments.
In diesem Beispiel ist :is(#sidebar, .card)
so spezifisch wie sein spezifischstes Argument, nämlich #sidebar
, und wird daher zu (1,0,0)
. Kombinieren Sie das mit der Spezifität von img
, also (0,0,1)
, und Sie erhalten (1,0,1)
als Spezifität für den gesamten komplexen Selektor.
@scope (#sidebar, .card) {
& img { /* Specificity = (1,0,0) + (0,0,1) = (1,0,1) */
…
}
}
Der Unterschied zwischen :scope
und &
in @scope
Neben Unterschieden bei der Berechnung der Spezifität besteht ein weiterer Unterschied zwischen :scope
und &
darin, dass :scope
den übereinstimmenden Wurzelknoten des Gültigkeitsbereichs darstellt, während &
den Selektor darstellt, der zum Abgleichen des Wurzelknotens des Gültigkeitsbereichs verwendet wird.
Daher ist es möglich, &
mehrmals zu verwenden. Dies steht im Gegensatz zu :scope
, das nur einmal verwendet werden kann, da ein Wurzelelement für die Begrenzung nicht innerhalb eines anderen Wurzelelements für die Begrenzung verwendet werden kann.
@scope (.card) {
& & { /* Selects a `.card` in the matched root .card */
}
:scope :scope { /* ❌ Does not work */
…
}
}
Bereich ohne Vorspann
Wenn Sie Inline-Stile mit dem Element <style>
schreiben, können Sie den Geltungsbereich der Stilregeln auf das übergeordnete Element des <style>
-Elements beschränken, indem Sie kein Stammelement angeben. Dazu lassen Sie die Vorab-Ansage von @scope
aus.
<div class="card">
<div class="card__header">
<style>
@scope {
img {
border-color: green;
}
}
</style>
<h1>Card Title</h1>
<img src="…" height="32" class="hero">
</div>
<div class="card__content">
<p><img src="…" height="32"></p>
</div>
</div>
Im Beispiel oben sind die Regeln mit begrenztem Gültigkeitsbereich nur auf Elemente innerhalb des div
mit dem Klassennamen card__header
ausgerichtet, da div
das übergeordnete Element des <style>
-Elements ist.
@scope in der Kaskade
Innerhalb der CSS-Abfolge fügt @scope
auch ein neues Kriterium hinzu: Nähe zum Gültigkeitsbereich. Dieser Schritt erfolgt nach der Spezifität, aber vor der Reihenfolge der Erscheinung.
Gemäß Spezifikation:
Beim Vergleichen von Deklarationen in Stilregeln mit unterschiedlichen Wurzelelementen für den Gültigkeitsbereich gilt die Deklaration mit den wenigsten Generationen- oder Geschwisterelement-Hops zwischen dem Wurzelelement für den Gültigkeitsbereich und dem Subjekt der Stilregel mit Gültigkeitsbereich als Gewinner.
Dieser neue Schritt ist praktisch, wenn mehrere Varianten einer Komponente verschachtelt werden. In diesem Beispiel wird @scope
noch nicht verwendet:
<style>
.light { background: #ccc; }
.dark { background: #333; }
.light a { color: black; }
.dark a { color: white; }
</style>
<div class="light">
<p><a href="#">What color am I?</a></p>
<div class="dark">
<p><a href="#">What about me?</a></p>
<div class="light">
<p><a href="#">Am I the same as the first?</a></p>
</div>
</div>
</div>
In diesem kleinen Ausschnitt des Markups ist der dritte Link white
anstelle von black
, obwohl er ein untergeordnetes Element eines div
ist, auf das die Klasse .light
angewendet wurde. Das liegt am Kriterium „Reihenfolge der Erscheinung“, das die Kaskade hier verwendet, um den Gewinner zu ermitteln. Da .dark a
als letztes deklariert wurde, gewinnt es gemäß der Regel .light a
.
Mit dem Kriterium „Nähe zum Ziel“ ist das jetzt kein Problem mehr:
@scope (.light) {
:scope { background: #ccc; }
a { color: black;}
}
@scope (.dark) {
:scope { background: #333; }
a { color: white; }
}
Da beide a
-Selektoren mit Bereich dieselbe Spezifität haben, wird das Näherungskriterium für den Bereich aktiviert. Dabei werden beide Selektoren nach ihrer Nähe zum Wurzelelement der Gültigkeitsebene gewichtet. Für dieses dritte a
-Element ist es nur ein Hop zum .light
-Begrenzungsstamm, aber zwei zum .dark
-Stamm. Daher wird die a
-Auswahl in .light
verwendet.
Schlussbemerkung: Auswahl-, nicht Stilisolierung
Wichtig ist, dass @scope
die Reichweite der Auswahlen einschränkt, aber keine Stilisolierung bietet. Attribute, die an untergeordnete Elemente weitergegeben werden, werden auch über die Untergrenze der @scope
hinaus übernommen. Eine solche Property ist color
. Wenn Sie das Element innerhalb eines Donut-Bereichs deklarieren, wird color
weiterhin an untergeordnete Elemente innerhalb des Lochs des Donuts vererbt.
@scope (.card) to (.card__content) {
:scope {
color: hotpink;
}
}
Im Beispiel oben haben das Element .card__content
und seine untergeordneten Elemente die Farbe hotpink
, da sie den Wert von .card
übernehmen.
(Coverbild von rustam burkhanov auf Unsplash)